#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