설명
마우스로 상자를 만들면, 그 상자에 있는 도형을 Detection해준다.
아직까지는 삼각형, 사각형, 오각형, 육각형, 원만 Detection해준다.
코드
#include "opencv2/opencv.hpp"
#include <iostream>
#include <string.h>
using namespace cv;
using namespace std;
bool ldown = false, lup = false;
Mat img_input, img_gray;
Point corner1, corner2;
Rect box;
char * name = "polygons";
Point2f weightCenter(vector<Point> contour);
float distance(Point2f a, Point2f b);
int GetAngle(Point a, Point b, Point c);
void detection(String name, int size, Mat img_result, vector<Point2f> approx, vector<vector<Point>> contours, int index);
void polygonDetection(Mat img_input);
static void mouse_callback(int event, int x, int y, int, void* param);
//무게중심
Point2f weightCenter(vector<Point> contour) {
Point2f result;
for (int i = 0; i < contour.size(); i++) {
result.x += contour[i].x;
result.y += contour[i].y;
}
result.x /= contour.size();
result.y /= contour.size();
return result;
}
//거리구하기
float distance(Point2f a, Point2f b) {
return sqrt(pow(abs(a.x - b.x), 2) + pow(abs(a.y - b.y), 2));
}
//세 점이 주어질 때 사이 각도 구하기
int GetAngle(Point a, Point b, Point c)
{
Point ab = { b.x - a.x, b.y - a.y };
Point cb = { b.x - c.x, b.y - c.y };
float dot = (ab.x * cb.x + ab.y * cb.y); // dot product
float cross = (ab.x * cb.y - ab.y * cb.x); // cross product
float alpha = atan2(cross, dot);
return (int)floor(alpha * 180.0 / CV_PI + 0.5);
}
//변의 길이
void detection(String name, int size, Mat img_result, vector<Point2f> approx, vector<vector<Point>> contours, int index) {
cout << name << endl;
int last = 0;
for (int k = 0; k < size - 1; k++) {
line(img_result, approx[k], approx[k + 1], Scalar(0, 255, 0), 3);
cout << "lenght : [" << k << "] : " << distance(approx[k], approx[k + 1]) << endl;
last = k;
}
line(img_result, approx[0], approx[approx.size() - 1], Scalar(0, 255, 0), 3);
cout << "lenght : [" << size - 1 << "]" << " : " << distance(approx[0], approx[approx.size() - 1]) << endl;
cout << "weight Center : " << weightCenter(contours[index]) << endl;
}
//도형 Detection
void polygonDetection(Mat img_input) {
cvtColor(img_input, img_gray, COLOR_BGR2GRAY);
threshold(img_gray, img_gray, 125, 255, THRESH_BINARY_INV | THRESH_OTSU);
vector<vector<Point>> contours;
findContours(img_gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
//contour를 근사화한다.
Mat img_result;
img_result = img_input.clone();
vector<Point2f> approx;
for (size_t i = 0; i < contours.size(); i++) {
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true) * 0.02, true);
//면적이 일정크기 이상이어야 한다.
if (fabs(contourArea(Mat(approx))) > 10) {
int size = approx.size();
vector<int> angle;
for (int k = 0; k < size; k++) {
int ang = GetAngle(approx[k], approx[(k + 1) % size], approx[(k + 2) % size]);
angle.push_back(ang);
}
sort(angle.begin(), angle.end());
int minAngle = angle.front();
int maxAngle = angle.back();
int threshold = 50;
//도형을 판정
if (size == 3 && (minAngle >= 0 - threshold && maxAngle <= 90 + threshold)) {
detection("traingle", size, img_result, approx, contours, i);
}
else if (size == 4 && minAngle >= 90 - threshold && maxAngle <= 90 + threshold) {
detection("rectangle", size, img_result, approx, contours, i);
}
else if (size == 5 && minAngle >= 108 - threshold && maxAngle <= 108 + threshold) {
detection("pentagon", size, img_result, approx, contours, i);
}
else if (size >= 6 && minAngle >= 120 - threshold && maxAngle <= 120 + threshold) {
cout << "circle" << endl;
cout << "Center : " << weightCenter(contours[i]) << endl;
cout << "radius : " << distance(weightCenter(contours[i]), approx[0]) << endl;
circle(img_result, weightCenter(contours[i]), distance(weightCenter(contours[i]), approx[0]), Scalar(0, 255, 0), 3);
}
printf("\n");
}
}
destroyWindow("Detection Polygons");
namedWindow("Detection Polygons", WINDOW_AUTOSIZE);
imshow("Detection Polygons", img_result);
}
static void mouse_callback(int event, int x, int y, int, void* param) {
if (event == EVENT_LBUTTONDOWN) {
ldown = true;
corner1.x = x;
corner1.y = y;
}
if (event == EVENT_LBUTTONUP) {
if (abs(x - corner1.x) > 20 && abs(y - corner1.y) > 20) {
lup = true;
corner2.x = x;
corner2.y = y;
}
else {
ldown = false;
}
}
if (ldown == true && lup == false) {
Point pt;
pt.x = x;
pt.y = y;
Mat locale_img = img_input.clone();
rectangle(locale_img, corner1, pt, Scalar(255, 0, 0));
imshow(name, locale_img);
}
if (ldown == true && lup == true) {
box.width = abs(corner1.x - corner2.x);
box.height = abs(corner1.y - corner2.y);
box.x = min(corner1.x, corner2.x);
box.y = min(corner1.y, corner2.y);
Mat crop(img_input, box);
polygonDetection(crop);
ldown = false; lup = false;
}
}
int main(int args, char *argv[]){
img_input = imread("polygons/all_pologon.png", CV_LOAD_IMAGE_COLOR);
if (img_input.empty())
{
cout << "Could not open or find the image" << std::endl;
return -1;
}
namedWindow(name, WINDOW_AUTOSIZE);
imshow(name, img_input);
setMouseCallback(name, mouse_callback);
waitKey(0);
return 0;
}
결과영상