存储由简单多边形组成的二进制图像的详细信息
Posted
技术标签:
【中文标题】存储由简单多边形组成的二进制图像的详细信息【英文标题】:Store details of a binary image consisting simple polygons 【发布时间】:2016-02-12 07:31:27 【问题描述】:这个问题与一些基于实践和经验的过程有关。我有一个 Mat 二值图像,它由黑色背景中的简单白色多边形组成。实际上,这些多边形代表报纸页面上的一篇文章。所以我想要的是将文章位置的详细信息存储在报纸页面内。一张 Mat 图像中只有一个多边形。所以一种选择是
-
使用纯 OpenCV 调用将 Mat 存储到 .xml 或 .yml 文件中(How to write a Float Mat to a file in OpenCV:接受的答案)
查找有顶点的多边形的坐标,并仅将这些坐标存储到数据库中
以下是我要存储的垫子的示例图像。
第一种方法似乎可行,但我不知道如何实现第二种方法。如果可能的话,那将是最有效的,因为我认为每篇文章只需要保存很少的坐标。我可以实现一个复杂的过程来找到它的顶点,并在需要时使用这些坐标重新绘制 Mat 图像。但我希望opencv中有一个简单的过程来完成这个任务。
所以我想知道哪种方法更好,如果第二种方法更好,如何在 opencv 中使用 c++ 做到这一点。我既不是 opencv 专家也不是 c++ 专家,所以一个合适的答案可以节省我很多时间和程序的效率。
【问题讨论】:
使用 findContours 和 method 参数作为 CV_CHAIN_APPROX_SIMPLE。在文档中说 CV_CHAIN_APPROX_SIMPLE 压缩水平、垂直和对角线段,只留下它们的端点。例如,一个直立的矩形轮廓用 4 个点进行编码。 findContours 方法在这里的输出是什么?我对这个例子很好奇>docs.opencv.org/2.4/doc/tutorials/imgproc/shapedescriptors/… findContours 和 CV_CHAIN_APPROX_SIMPLE 很可能会输出类似于您在第二种方法中所期望的内容。 @dhanushka 所以它应该给出一组点坐标。但我怀疑该方法的哪个参数输出这些坐标。是OutputArrayOfArrays 轮廓参数吗?? 是的。在您之前提到的示例链接中,它是vector<vector<Point> > contours;
。
【参考方案1】:
您可以简单地使用findContours,并使用适当的轮廓近似方法。基本上,除了将存储所有点的 CV_CHAIN_APPROX_NONE
之外,其他所有方法都适用于此示例:CV_CHAIN_APPROX_SIMPLE
、CV_CHAIN_APPROX_TC89_L1
和 CV_CHAIN_APPROX_TC89_KCOS
。
您可以将这些点存储在您的数据库中。然后您可以重新加载这些点,并使用fillPoly 绘制原始图像。
这个简单的例子展示了使用近似方法检索到的轮廓点,以及如何使用这些点重新绘制图像。
注意您的图像有别名(您可能在 png 之前将其保存在 jpeg 中),因此您需要删除别名,例如仅保留值等于 255 的点。
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main()
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
// Removing compression artifacts
img = img == 255;
vector<vector<Point>> contours;
findContours(img.clone(), contours, RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
if (contours.empty()) return -1;
// Save the vertices "contours[0]"
// Result image
Mat3b res;
cvtColor(img, res, COLOR_GRAY2BGR);
for (int i = 0; i < contours[0].size(); ++i)
circle(res, contours[0][i], 3, Scalar(0,255,0));
// Reconstruct image from contours vertex
// Load the vertices
vector<vector<Point>> vertices = contours[0] ;
Mat1b rec(img.rows, img.cols, uchar(0));
fillPoly(rec, vertices, Scalar(255));
imshow("Vertices", res);
imshow("Reconstructed", rec);
waitKey();
return 0;
轮廓近似法的绿色顶点:
【讨论】:
嗨三木.. :-) 非常感谢您的回答。我会回复结果。顺便说一句,在找到它们或重新生成图像时,是否有可能丢失任何顶点或将它们的坐标稍微移开? @SamithaChathuranga 不,顶点和 重构 矩阵将是一致的。请记住将原始图像保存为 png(或任何其他无损格式)以避免压缩伪影,其删除会修改原始形状并给您不准确的顶点。 好的,是的,我正在以 .png 格式保存图像(它被转换为 jpg 只是为了上传到问题中)【参考方案2】:一种有点不合时宜的方法...您可以轻松地将 Mat 保存为 OpenCV 中的图像 - 最好是 PGM
或 PNG
,因为它们是无损的。然后,您可以将图像传递给像 potrace
这样的矢量跟踪程序,让它以 SVG 格式告诉您轮廓并将其存储在您的数据库中。
所以,potrace
喜欢 PGM
文件,因此您可以将大纲保存为 OpenCV 中的 PGM
或 PNG
,然后使用 ImageMagick 将其制成 PGM 并将其传递给 @987654330 @像这样:
convert OpenCVImage.png pgm:- | potrace - -b svg -o file.svg
这会给你一个像这样的svg
文件:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 3486.000000 4747.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.13, written by Peter Selinger 2001-2015
</metadata>
<g transform="translate(0.000000,4747.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M0 23735 l0 -23735 17430 0 17430 0 0 23735 0 23735 -17430 0 -17430
0 0 -23735z m20980 6560 l0 -3415 -399 0 c-293 0 -402 3 -407 12 -7 11 -68 11
-2391 -9 l-781 -6 -6 -6576 c-3 -3617 -9 -6840 -12 -7163 l-6 -588 -1939 0
-1939 0 0 10580 0 10580 3940 0 3940 0 0 -3415z"/>
</g>
</svg>
顺便说一句,您可以在网络浏览器中查看。
您可以随时调用图像并使用 ImageMagick 或其他工具在命令行中重新创建它,如下所示:
convert outline.svg outline.png
我会注意到,您的整个 PNG
实际上只有 32kB 并且存储空间非常便宜,因此生成矢量图像以节省空间似乎不值得费心。事实上,如果你使用像 ImageMagick 这样不错的工具并将你的图像转换为一个位 PNG,它会降到 6,150 字节,这非常小......
convert YourBigInefficientOutline.png NiceImageMagickOutlineOf6kB.png
而且,如果您可以将轮廓缩小到原始尺寸的 1/5,这仍然可能足以找到报纸文章,您可以这样做:
convert YourBig.png -resize 700x900 MySmall.png
只有 1,825 个字节。
【讨论】:
顺便说一句,OpenCV 中内置的 Harris Corner Detector 可以很好地找到角落...github.com/Itseez/opencv/blob/master/samples/cpp/tutorial_code/… 然后您可以使用 "Gift Wrapping 重新创建轮廓算法”在这里...en.wikipedia.org/wiki/Gift_wrapping_algorithm 我会试试的。谢谢。顺便说一句,您回答中的过程很有趣。但似乎它使事情变得更加复杂,因为存储原始 .png 图像似乎很便宜。特别是使用其他工具会使事情变得更加复杂【参考方案3】:cv::Mat inputImage = cv::imread("input.png", CV_LOAD_IMAGE_GRAYSCALE);
// find non-zero point coordinates
cv::Mat nonZeroCoordinates;
cv::findNonZero(inputImage, nonZeroCoordinates);
然后您可以将nonZeroCoordinates
矩阵保存到您的文件中以供使用。
如果你想使用这些坐标创建相同的图像,你可以这样做:
std::vector<std::vector<cv::Point> > points;
points.push_back(nonZeroCoordinates);
cv::Mat output = cv::Mat::zeros(inputImage.size(), CV_8UC1);
cv::fillPoly(output, points, cv::Scalar(255));
希望对你有帮助!
【讨论】:
如我所见,这与保留原始 Mat 对象没有太大区别。在这里,您正在创建一个新的 Mat 对象 nonZeroCoordinates 来表示原始对象(这可能会占用更少的空间),然后使用它来重现原始 Mat。我想将详细信息存储在一个文件中。 你在寻找什么样的细节? 多边形顶点的坐标。这就是我想要存储在数据库或文件中然后使用回来重新生成原始图像的内容。 您是否尝试过寻找轮廓然后使用convexHull?在这里参考:***.com/questions/33625186/…【参考方案4】:这取决于多边形的通用性。如果多边形的边缘始终平行于x
和y
轴,那么您可以查看特定像素的 8 邻域中的像素,如果有奇数个白色像素,您就找到了一个角。或者使用 4 邻域并测试偶数个白色像素。
【讨论】:
感谢您的宝贵提示。但如果可用,我希望有一种特定于 opencv 的方法。以上是关于存储由简单多边形组成的二进制图像的详细信息的主要内容,如果未能解决你的问题,请参考以下文章