Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
#include <opencv2/opencv.hpp>
#include <cstdio>
#include <algorithm>
using Contour = std::vector<cv::Point>;
void extractBlueRegion(
const cv::Mat &src, cv::Mat &dst, uchar bLow, uchar bHigh)
{
CV_Assert(!src.empty() && src.type() == CV_8UC3);
CV_Assert(bLow <= bHigh);
std::vector<cv::Mat> planes;
cv::split(src, planes);
cv::Mat &b = planes[0];
cv::threshold(b, dst, bLow, 255, cv::THRESH_TOZERO);
cv::threshold(dst, dst, bHigh, 255, cv::THRESH_TOZERO_INV);
cv::threshold(dst, dst, 0, 255, cv::THRESH_BINARY);
}
double distance(cv::Point2d a, cv::Point2d b)
{
return std::sqrt(std::pow(a.x - b.x, 2) + std::pow(a.y - b.y, 2));
}
cv::Point2d getCentroid(const Contour &contour)
{
cv::Moments m = cv::moments(contour, false);
return cv::Point2d(m.m10 / m.m00, m.m01 / m.m00);
}
void orderContoursByDistanceToCenter(std::vector<Contour> &contours, const cv::Mat &image)
{
cv::Point2d center(image.cols / 2.0, image.rows / 2.0);
std::sort(contours.begin(), contours.end(), [center](const Contour &a, const Contour &b) -> int
{
double aDist = distance(center, getCentroid(a));
double bDist = distance(center, getCentroid(b));
return (aDist < bDist);
});
}
double pixelToKilometer(double pixelLength, double z)
{
// google mapsのz値(100倍)の値と、1ピクセルあたりのkm縮尺の対
static std::unordered_map<int, double> lut;
lut[775] = 50 / 97.0; // 50kmで97px の縮尺だった
lut[1075] = 5 / 80.0;
lut[1000] = 10 / 88.0;
lut[1200] = 2 / 77.0;
// zを100倍にし、25刻みにする (小数のキーを一応避ける)
int zInteger = int(z);
double zDecimal = z - zInteger;
int key = (zInteger * 100) + (int((zDecimal + 0.125/*四捨五入的な*/) * 4) * 100 / 4);
if (lut.find(key) != lut.end())
return pixelLength * lut[key];
return -1;
}
void drawSingleContour(const Contour &contour, const cv::Mat &src, cv::Mat &dst)
{
src.copyTo(dst);
std::vector<Contour> contours = { contour };
cv::drawContours(dst, contours, -1, cv::Scalar(0, 0, 255), 2);
}
int main(int argc, char* argv[])
{
if (argc < 2)
return EXIT_FAILURE;
const char *file = argv[1];
cv::Mat src = cv::imread(file);
// 海領域
cv::Mat blueRegion;
extractBlueRegion(src, blueRegion, 250,255);
// 陸に置き換える
cv::Mat land;
cv::bitwise_not(blueRegion, land);
// 輪郭
std::vector<Contour> contours;
cv::findContours(land, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
//cv::Mat view = src.clone();
//cv::drawContours(view, contours, -1, cv::Scalar(0, 0, 255), 3);
orderContoursByDistanceToCenter(contours, src);
const Contour &islandContour = contours[0];
double perimeter = cv::arcLength(islandContour, true);
//double kilometer = pixelToKilometer(perimeter, 10.75);
//double kilometer = pixelToKilometer(perimeter, 7.75);
double kilometer = pixelToKilometer(perimeter, 12.00);
if (kilometer < 0)
{
std::puts("pixelToKilometer失敗(z値未登録?)");
return EXIT_FAILURE;
}
std::printf("周囲長: %.2fkm\n", kilometer);
cv::Mat view;
drawSingleContour(islandContour, src, view);
cv::imshow("blueRegion", blueRegion);
cv::imshow("view", view);
cv::waitKey();
cv::destroyAllWindows();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment