Last active
December 22, 2015 01:41
-
-
Save shimat/15c407a2a4390419ce0e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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