我是图像处理世界的新手,我有一个问题陈述,我需要先行解决它。
问题陈述:
我有一个由图案组成的图像。使用不同的单个形状创建此模式。下面是用于形成图案的图案和个别形状。
详细问题陈述:
我有15个独特的形状(下图),我可以使用它绘制不同的图案(已经给出了一个例子)。我有超过400种模式。我想使用图像处理来找出用于生成特定图案的不同形状(及其在图案中的位置)。
我想要实现的目标:
我想输入图案图像并找出用于形成图案的各个形状以及形状放置在图案中的位置?:
注意:由于问题变得太大,我没有包含所有单独的形状。
模式图片:
答案 0 :(得分:3)
要了解哪些参考形状构成您的图像,您可以
对于本答案的范围,我使用这些已经预处理过的图像。第一张图片是简单的阈值,第二张图片使用了this代码段。
在预处理的图像上找到中心点非常简单。您可以使用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;
}
答案 1 :(得分:1)
考虑到您有一个形状字典,您可以通过模板匹配来查找图像中的哪个形状。模板匹配相当简单:你计算一个'#34;拟合优度&#34;对于形状和图像,测量诸如相关性,均方误差等,其形状位于图像中的每个点。请参阅示例these lecture notes。如果使用相关(在这种情况下有意义),您可以使用FFT显着加快计算速度。请查看此OpenCV tutorial。
的示例以上假设模板的尺寸和方向与图像中所示的相同。如果尺寸可能不同,则需要使用多尺度模板匹配方法,我稍微涉及但不困难。只需尝试在不同的比例和方向上多次匹配每个形状。或者旋转和缩放图像。我认为,根据你的例子,你只需要测试4个方向和1个尺度,所以这是一个合理的方法。
更灵活的方法是检测点(例如使用模板匹配),在点周围填充以填充形状(假设它们都是简单的多边形),并提取检测区域的边界。然后可以使用例如字典中的边界匹配该边界。 Fourier descriptors。这将允许您以任意比例和方向检测形状。
答案 2 :(得分:1)