我想检测这种模式
正如你所看到的,它基本上是字母C,在另一个内部,具有不同的方向。我的模式可以在彼此内部有多个C,我用2 C发布的只是一个样本。我想知道有多少C,以及每个C的方向。现在我已经设法检测到这种模式的中心,基本上我已经设法检测到最里面的C的中心。你能不能向我提供关于我可以使用的不同算法的任何想法?
答案 0 :(得分:14)
我们走了!此方法的高级概述可以描述为顺序执行以下步骤:
由于我分享了源代码,因此我不想详细介绍,所以请随意以您喜欢的方式进行测试和更改。 让我们开始,冬天来了:
#include <iostream>
#include <vector>
#include <cmath>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
cv::RNG rng(12345);
float PI = std::atan(1) * 4;
void isolate_object(const cv::Mat& input, cv::Mat& output)
{
if (input.channels() != 1)
{
std::cout << "isolate_object: !!! input must be grayscale" << std::endl;
return;
}
// Store the set of points in the image before assembling the bounding box
std::vector<cv::Point> points;
cv::Mat_<uchar>::const_iterator it = input.begin<uchar>();
cv::Mat_<uchar>::const_iterator end = input.end<uchar>();
for (; it != end; ++it)
{
if (*it) points.push_back(it.pos());
}
// Compute minimal bounding box
cv::RotatedRect box = cv::minAreaRect(cv::Mat(points));
// Set Region of Interest to the area defined by the box
cv::Rect roi;
roi.x = box.center.x - (box.size.width / 2);
roi.y = box.center.y - (box.size.height / 2);
roi.width = box.size.width;
roi.height = box.size.height;
// Crop the original image to the defined ROI
output = input(roi);
}
有关isolate_object()
的实施的详细信息,请check this thread。 cv::RNG
稍后将用于fill each contour with a different color和PI
,嗯......您知道 PI 。
int main(int argc, char* argv[])
{
// Load input (colored, 3-channel, BGR)
cv::Mat input = cv::imread("test.jpg");
if (input.empty())
{
std::cout << "!!! Failed imread() #1" << std::endl;
return -1;
}
// Convert colored image to grayscale
cv::Mat gray;
cv::cvtColor(input, gray, CV_BGR2GRAY);
// Execute a threshold operation to get a binary image from the grayscale
cv::Mat binary;
cv::threshold(gray, binary, 128, 255, cv::THRESH_BINARY);
二进制图像看起来与输入完全相同,因为它只有2种颜色(B&amp; W):
// Find the contours of the C's in the thresholded image
std::vector<std::vector<cv::Point> > contours;
cv::findContours(binary, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
// Fill the contours found with unique colors to isolate them later
cv::Mat colored_contours = input.clone();
std::vector<cv::Scalar> fill_colors;
for (size_t i = 0; i < contours.size(); i++)
{
std::vector<cv::Point> cnt = contours[i];
double area = cv::contourArea(cv::Mat(cnt));
//std::cout << "* Area: " << area << std::endl;
// Fill each C found with a different color.
// If the area is larger than 100k it's probably the white background, so we ignore it.
if (area > 10000 && area < 100000)
{
cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255));
cv::drawContours(colored_contours, contours, i, color,
CV_FILLED, 8, std::vector<cv::Vec4i>(), 0, cv::Point());
fill_colors.push_back(color);
//cv::imwrite("test_contours.jpg", colored_contours);
}
}
colored_contours 的内容如下:
// Create a mask for each C found to isolate them from each other
for (int i = 0; i < fill_colors.size(); i++)
{
// After inRange() single_color_mask stores a single C letter
cv::Mat single_color_mask = cv::Mat::zeros(input.size(), CV_8UC1);
cv::inRange(colored_contours, fill_colors[i], fill_colors[i], single_color_mask);
//cv::imwrite("test_mask.jpg", single_color_mask);
由于此for
循环执行两次,每个颜色用于填充轮廓,我希望您查看此阶段生成的所有图像。所以下面的图像是由 single_color_mask 存储的图像(每次迭代循环一次):
// Crop image to the area of the object
cv::Mat cropped;
isolate_object(single_color_mask, cropped);
//cv::imwrite("test_cropped.jpg", cropped);
cv::Mat orig_cropped = cropped.clone();
这些是由裁剪存储的(顺便说一句,较小的C看起来很胖,因为此页面重新调整图像以使其具有与较大C相同的尺寸,不要&#39 ;担心):
// Figure out the center of the image
cv::Point obj_center(cropped.cols/2, cropped.rows/2);
//cv::circle(cropped, obj_center, 3, cv::Scalar(128, 128, 128));
//cv::imwrite("test_cropped_center.jpg", cropped);
为了更清楚地了解 obj_center 的用途,我在该位置绘制了一个用于教育目的的小灰圈:
// Figure out the exact center location of the border
std::vector<cv::Point> border_points;
for (int y = 0; y < cropped.cols; y++)
{
if (cropped.at<uchar>(obj_center.x, y) != 0)
border_points.push_back(cv::Point(obj_center.x, y));
if (border_points.size() > 0 && cropped.at<uchar>(obj_center.x, y) == 0)
break;
}
if (border_points.size() == 0)
{
std::cout << "!!! Oops! No border detected." << std::endl;
return 0;
}
// Figure out the exact center location of the border
cv::Point border_center = border_points[border_points.size() / 2];
//cv::circle(cropped, border_center, 3, cv::Scalar(128, 128, 128));
//cv::imwrite("test_border_center.jpg", cropped);
上面的过程从图像的顶部/中间扫描单个垂直线,以找到圆的边界,以便能够计算它的宽度。同样,出于教育目的,我在边界中间画了一个小灰圈。这是裁剪的样子:
// Scan the border of the circle for discontinuities
int radius = obj_center.y - border_center.y;
if (radius < 0)
radius *= -1;
std::vector<cv::Point> discontinuity_points;
std::vector<int> discontinuity_angles;
for (int angle = 0; angle <= 360; angle++)
{
int x = obj_center.x + (radius * cos((angle+90) * (PI / 180.f)));
int y = obj_center.y + (radius * sin((angle+90) * (PI / 180.f)));
if (cropped.at<uchar>(x, y) < 128)
{
discontinuity_points.push_back(cv::Point(y, x));
discontinuity_angles.push_back(angle);
//cv::circle(cropped, cv::Point(y, x), 1, cv::Scalar(128, 128, 128));
}
}
//std::cout << "Discontinuity size: " << discontinuity_points.size() << std::endl;
if (discontinuity_points.size() == 0 && discontinuity_angles.size() == 0)
{
std::cout << "!!! Oops! No discontinuity detected. It's a perfect circle, dang!" << std::endl;
return 0;
}
很好,所以上面的代码片段沿着圆圈的边缘扫描,寻找不连续性。我分享了一张示例图片来说明我的意思。图像上的每个灰点代表一个经过测试的像素。当像素是黑色时,意味着我们发现了不连续性:
// Figure out the approximate angle of the discontinuity:
// the first angle found will suffice for this demo.
int approx_angle = discontinuity_angles[0];
std::cout << "#" << i << " letter C is rotated approximately at: " << approx_angle << " degrees" << std::endl;
// Figure out the central point of the discontinuity
cv::Point discontinuity_center;
for (int a = 0; a < discontinuity_points.size(); a++)
discontinuity_center += discontinuity_points[a];
discontinuity_center.x /= discontinuity_points.size();
discontinuity_center.y /= discontinuity_points.size();
cv::circle(orig_cropped, discontinuity_center, 2, cv::Scalar(128, 128, 128));
cv::imshow("Original crop", orig_cropped);
cv::waitKey(0);
}
return 0;
}
很好......最后一段代码负责确定不连续的近似角度以及指示不连续的中心点。以下图像由 orig_cropped 存储。我再次添加了一个灰点,以显示检测到的确切位置作为间隙的中心:
执行时,此应用程序将以下信息显示在屏幕上:
#0 letter C is rotated approximately at: 49 degrees
#1 letter C is rotated approximately at: 0 degrees
我希望它有所帮助。
答案 1 :(得分:0)
首先,您可以使用Hough转换。这个算法不是很快,但它非常强大。特别是如果你有这样清晰的图像。
一般方法是:
1) preprocessing - suppress noise, convert to grayscale / binary
2) run edge detector
3) run Hough transform - IIRC it's `cv::HoughCircles` in OpenCV
4) do some postprocessing - remove surplus circles, decide which ones correspond to shape of letter C, and so on
我的方法将为每个字母C提供2个hough圆圈。一个在内部边界上,一个在外部字母C上。如果每个字母只需要一个圆圈,则可以使用骨架化算法。更多信息http://homepages.inf.ed.ac.uk/rbf/HIPR2/skeleton.htm
答案 2 :(得分:0)
鉴于我们已经嵌套了C结构并且您知道Cs的中心并且想要评估方向 - 只需要观察沿着同心Cs的 radius 的像素分布。所有方向。
这可以通过从中心执行简单的morphological dilation操作来完成。当我们到达最里面的C的正确半径时,我们将达到最内层C所覆盖的最大像素数。盘和C之间的差异将给出我们整体中间隙的位置,并且可以执行{ {3}}获得C中间隙的质心。中心与此点之间的角度是C的方向。迭代此步骤直到覆盖所有Cs。
这也可以使用Cs中心点的ultimate erosion快速完成。