我是一个图像处理世界的新手,我有一个问题陈述,我需要一个先开始解决它。
问题陈述:
我有一个由图案组成的图像。此模式是使用不同的单个形状创建的。下面是用于形成模式的模式和个人形状。
详细问题陈述
我有15个独特的形状(下图),我可以用它画出不同的图案(已经给出了一个例子)。我有400多个图案。我想使用图像处理来找出用于生成特定图案的不同形状(及其在模式中的位置)。
所有独特的形状:

更多的模式图像

我想要实现的:
我想输入图案图像,找出用于形成图案的个别形状和形状放在图案中的位置?:
注意:,我没有包括所有的个别形状,因为问题变得太大了。
模式图像:

个人形状:



发布于 2017-11-04 20:30:05
要知道哪些参考形状组成您的图像,您可以
对于这个答案的范围,我使用这些已经预处理的图像。第一个图像是简单的阈值化,第二个我使用了这片段。


在预处理后的图像上很容易找到中心点。您可以使用cv::connectedComponentsWithStats检索所有黑色组件,然后删除太大的组件。您可以在下面的函数getCenterPoints中找到代码。

然后,只要简单地将此图像和原始图像组合起来,就可以很容易地获得轮廓(稍后需要):

现在我们可以找到点,但我们也需要一种方式来判断哪一个形状构成了最终的图像。我们可以使用形状的几何结构为每个形状构建一个简单的描述符:我们保存一个Mat 4值,表示中心在垂直和水平方向上与轮廓之间的距离:

这唯一地标识了所有参考形状。然后,我们对这个4元向量进行规范化,使它成为尺度不变的。使用这个描述符可以避免繁琐的“多尺度模板匹配”,而且速度更快,扩展性更强。您可以在下面的函数computeShapeDescriptor中找到这方面的代码。
为了计算形状描述符,我们还需要形状中心的正确位置,这就是我们前面发现的blob的质心。我们基本上再次使用cv::connectedComponentWithStats。见下面的getCentroids。
现在我们知道了如何找到点来定位所有的形状,并知道如何描述它们。要在图像中找到相应的参考形状,只需比较描述符。最相似的一个应该是正确的!




供参考的完整代码:
#include <opencv2\opencv.hpp>
#include <vector>
void computeShapeDescriptor(const cv::Mat1b shape_outline, cv::Point center, cv::Mat1d& desc)
{
desc = cv::Mat1d(1, 4, 0.0);
// Go up until I find a outline pixel
for (int i = center.y; i >= 0; --i) {
if (shape_outline(i, center.x) > 0) {
desc(0) = std::abs(i - center.y);
break;
}
}
// Go right until I find a outline pixel
for (int i = center.x; i < shape_outline.cols; ++i) {
if (shape_outline(center.y, i) > 0) {
desc(1) = std::abs(i - center.x);
break;
}
}
// Go down until I find a outline pixel
for (int i = center.y; i < shape_outline.rows; ++i) {
if (shape_outline(i, center.x) > 0) {
desc(2) = std::abs(i - center.y);
break;
}
}
// Go left until I find a outline pixel
for (int i = center.x; i >= 0; --i) {
if (shape_outline(center.y, i) > 0) {
desc(3) = std::abs(i - center.x);
break;
}
}
desc /= cv::norm(desc, cv::NORM_L1);
}
void getCenterPoints(const cv::Mat1b& src, cv::Mat1b& dst)
{
dst = cv::Mat1b(src.rows, src.cols, uchar(0));
cv::Mat1i labels;
cv::Mat1i stats;
cv::Mat1d centroids;
int n_labels = cv::connectedComponentsWithStats(~src, labels, stats, centroids);
for (int i = 1; i < n_labels; ++i) {
if (stats(i, cv::CC_STAT_AREA) < 100)
{
dst.setTo(255, labels == i);
}
}
}
void getCentroids(const cv::Mat1b& src, cv::Mat1d& centroids)
{
// Find the central pixel
cv::Mat1i labels;
cv::Mat1i stats;
cv::connectedComponentsWithStats(src, labels, stats, centroids);
// 'centroids' contains in each row x,y coordinates of the centroid
}
int main()
{
// Load the reference shapes
cv::Mat1b reference = cv::imread("path_to_reference_shapes", cv::IMREAD_GRAYSCALE);
// -------------------------
// Compute descriptor for each reference shape
// -------------------------
// Get the centers
cv::Mat1b reference_centers;
getCenterPoints(reference, reference_centers);
// Get the centroids
cv::Mat1d shape_centroids;
getCentroids(reference_centers, shape_centroids);
// Find the outline
cv::Mat1b reference_outline = ~(reference | reference_centers);
// Prepare output image
cv::Mat3b reference_output;
cv::cvtColor(reference, reference_output, cv::COLOR_GRAY2BGR);
// Compute the descriptor for each shape
std::vector<cv::Mat1f> shape_descriptors;
for (int i = 1; i < shape_centroids.rows; ++i)
{
cv::Point center;
center.x = std::round(shape_centroids(i, 0));
center.y = std::round(shape_centroids(i, 1));
cv::Mat1d desc;
computeShapeDescriptor(reference_outline, center, desc);
shape_descriptors.push_back(desc.clone());
// Draw the ID of the shape
cv::putText(reference_output, cv::String(std::to_string(i)), center, cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 0, 255));
}
// -------------------------
// Find shapes in image
// -------------------------
cv::Mat1b img = cv::imread("path_to_image", cv::IMREAD_GRAYSCALE);
// Get the centers
cv::Mat1b img_centers;
getCenterPoints(img, img_centers);
// Get the centroids
cv::Mat1d img_centroids;
getCentroids(img_centers, img_centroids);
// Find the outline
cv::Mat1b img_outline = ~(img | img_centers);
// Prepare output image
cv::Mat3b img_output;
cv::cvtColor(img, img_output, cv::COLOR_GRAY2BGR);
// Compute the descriptor for each found shape, and assign to nearest descriptor among reference shapes
for (int i = 1; i < img_centroids.rows; ++i)
{
cv::Point center;
center.x = std::round(img_centroids(i, 0));
center.y = std::round(img_centroids(i, 1));
cv::Mat1d desc;
computeShapeDescriptor(img_outline, center, desc);
// Compute the distance with all reference descriptors
double minDist = 1e10;
int minIdx = 0;
for (size_t j = 0; j < shape_descriptors.size(); ++j)
{
// Actual distance computation
double dist = 0.0;
for (int c = 0; c < desc.cols; ++c) {
dist += std::abs(desc(c) - shape_descriptors[j](c));
}
if (minDist > dist) {
minDist = dist;
minIdx = j;
}
}
// Draw the ID of the shape
cv::putText(img_output, cv::String(std::to_string(minIdx + 1)), center, cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 0, 255, 255));
}
return 0;
}发布于 2017-11-04 15:34:30
您可以使用点定位单个形状(二值化+连接组件标记)。
然后,很容易检测到方角的缺失/存在,例如通过相邻形状对之间的小窗口。这将给您一个二进制代码,您可以使用它来区分您的模式。

发布于 2017-11-04 15:11:37
考虑到您有一个形状字典,您可以模板匹配,以找到哪个形状是在您的图像。模板匹配非常简单:计算形状和图像的相关性、均方误差等“拟合优度”度量,形状位于图像的每个点。例如,参见这些讲稿。如果使用相关性(在这种情况下这是有意义的),则可以使用FFT来显著加快计算速度。例如,看看这个OpenCV教程。
以上假设模板的大小和方向与图像中显示的相同。如果大小可能不同,您需要使用多尺度模板匹配方法,这一点我稍微多了一点,但并不困难。只要试着在不同的尺度和方向上多次匹配每个形状。或者旋转和缩放你的图像。我猜想,给出你的例子,你只需要测试4个方向和1个尺度,所以这是一个合理的方法。
更灵活的方法是检测点(例如,模板匹配),填充周围的点来填充形状(假设它们都是简单的多边形),并提取检测区域的边界。然后,可以使用例如傅里叶描述符将该边界与字典中的边界相匹配。这将允许您检测任意尺度和方向的形状。
https://stackoverflow.com/questions/47108493
复制相似问题