一、XML文件的读取
Reference:
1、OpenCV文件的读取(https://jingyan.baidu.com/article/22a299b5f4d07e9e18376a7f.html)
二、OpenCV挖圆算法
Reference:
1、找圆算法(HoughCircles)总结与优化(http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096)
2、opencv图像处理学习(五十一)——拟合(opencv自带函数)(https://blog.youkuaiyun.com/qq_35789421/article/details/89374491)
3、opencv之坑(四)——拟合圆(https://blog.youkuaiyun.com/qq_35789421/article/details/89383512)
4、OpenCV3.0 Examples学习笔记(11)-houghcircles.cpp-houghcircles函数实现圆形检测(https://blog.youkuaiyun.com/u012566751/article/details/54379748)此篇文章介绍HoughCircles
5、Opencv2.4.9源码分析——HoughCircles(https://blog.youkuaiyun.com/zhaocj/article/details/50454847)此篇文章介绍HoughCircles算法思路。
图形可以用一些参数进行表示,标准霍夫变换的原理就是把图像空间转换成参数空间(即霍夫空间),例如霍夫变换的直线检测就是在距离-角度空间内进行检测。圆可以表示成:(x-a)^2+(y-b)^2=r^2 (1)
其中a和b表示圆心坐标,r表示圆半径,因此霍夫变换的圆检测就是在这三个参数组成的三维空间内进行检测。
原则上,霍夫变换可以检测任何形状。但复杂的形状需要的参数就多,霍夫空间的维数就多,因此在程序实现上所需的内存空间以及运行效率都不利于把标准霍夫变换应用于实际复杂图形的检测中。所以一些改进的霍夫变换就相继提出,它们的基本原理就是尽可能减少霍夫空间的维数。
HoughCircles函数实现了圆形检测,它使用的算法也是改进的霍夫变换--------2-1霍夫变换(21HT),也就是把霍夫变换分为两个阶段,从而减少了霍夫空间的维数。第一阶段用于检测圆心,第二阶段从圆心推导出圆半径。检测圆心的原理是圆心是它所在圆周所有法线的交汇处,因此只要找到这个角点,即可确定圆心,该方法所用的霍夫空间与图像空间的性质相同,因此它仅仅是二维空间。检测圆半径的方法是从圆心到圆周上的任意一点的距离(即半径)是相同,只要确定一个阈值。只要相同距离的数量大于该阈值,我们就认为该距离就是该圆心所对应的圆半径,该方法只需要计算半径直方图,不使用霍夫空间。圆心和半径都得到了,那么公式1一个圆形就得到了。从上面的分析可以看出,2-1霍夫变换把标准霍夫变换的三维霍夫空间缩小为二维霍夫空间,因此无论在内存的使用上还是运行效率上,2-1霍夫变换都远远优于标准霍夫变换。但该算法有一个不足之处就是由于圆半径的检测完全取决于圆心的检测,因此如果圆心检测出现偏差,那么圆半径的检测肯定也是错误的。2-1霍夫变换的具体步骤为:
第一阶段:检测圆心
(1.1)对输入图像边缘检测
(1.2)计算图形的梯度,并确定圆周线,其中圆周的梯度就是它的法线
(1.3)在二维霍夫空间内,绘出所有图形的梯度直线,某坐标点上累加和的值越大,说明在该点上直线相交的次数越多,也就是越有可能是圆心
(1.4)在霍夫空间的4邻域内进行非最大值抑制
(1.5)设定一个阈值,霍夫空间内累加和大于该阈值的点就对应于圆心。
第二阶段:检测圆半径
(2.1)计算某一个圆心到所有圆周线的距离,这些距离中就有该圆心所对应的圆的半径的值,这些半径值当然是相等的,并且这些圆半径的数量要远远大于其他距离相等的数量
(2.2)设定两个阈值,定义为最大半径和最小半径,保留距离在这两个半径之间的值,这意味着我们检测的圆不能太大,也不能太小
(2.3)对保留下来的距离进行排序
(2.4)找到距离相同的那些值,并计算相同值的数量
(2.5)设定一个阈值,只有相同值的数量大于该阈值,才认为该值是该圆心对应的圆半径
(2.6)对每一个圆心,完成上面的2.1~2.5步骤,得到所有的圆半径。
HoughCircles函数的原型为:
void HoughCircles(InputArray image,OutputArray circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0,int maxRadius=0 )
image为输入图像,要求是灰度图像
circles为输出圆向量,每个向量包括三个浮点型的元素——圆心横坐标,圆心纵坐标和圆半径
method为使用霍夫变换圆检测的算法,Opencv2.4.9只实现了2-1霍夫变换,它的参数是CV_HOUGH_GRADIENT
dp为第一阶段所使用的霍夫空间的分辨率,dp=1时表示霍夫空间与输入图像空间的大小一致,dp=2时霍夫空间是输入图像空间的一半,以此类推
minDist为圆心之间的最小距离,如果检测到的两个圆心之间距离小于该值,则认为它们是同一个圆心
param1为边缘检测时使用Canny算子的高阈值
param2为步骤1.5和步骤2.5中所共有的阈值
minRadius和maxRadius为所检测到的圆半径的最小值和最大值
Ref1中的文件Debug调试记录
1、
貌似opencv3.0以后std::vector了,所以需要在文件中加入一个命名空间:
namespace cv
{
using std::vector;
};
三、 连通域处理,connectedconponentsWithstates:
Ref
1、OpenCV中的轮廓提取新函数connectedComponentsWithStats的使用(https://blog.youkuaiyun.com/sy95122/article/details/85992617)
2、opencv阈值分割(https://blog.youkuaiyun.com/qq_35508344/article/details/82686335)
3、OpenCV连通域相关操作(https://www.cnblogs.com/didea/p/6195718.html)
4、OpenCV基础——threshold函数的使用(https://blog.youkuaiyun.com/u012566751/article/details/77046445)
5、OpenCV图像中寻找最大区域Max_Area(https://blog.youkuaiyun.com/augusdi/article/details/9005362)
6、opencv 查找连通区域 最大面积(https://blog.youkuaiyun.com/yangzm/article/details/82983534)
7、opencv 图像阈值分割图像(https://blog.youkuaiyun.com/qq_18343569/article/details/47174273)
采用两种方法:
方法1:findcontours
方法2:connectedComponentsWithStats
作者说方法一比方法二快
8、opencv轮廓提取与轮廓拟合 (https://blog.youkuaiyun.com/iteye_8075/article/details/82612157)
此教程给出了详细的轮廓提取与选择轮廓的代码
9、多边形拟合处理轮廓(https://blog.youkuaiyun.com/lingmengxiaotong/article/details/80166055)
四、孔洞填充算法
1、OpenCV中孔洞填充算法(https://www.cnblogs.com/wangyaning/p/7853942.html)
2、
五、边缘检测
1、OpenCV Canny边缘检测(https://blog.youkuaiyun.com/rongpeisheng666/article/details/81625102)
2、opencv(8)-Canny边缘检测+直线检测+圆检测+轮廓发现(https://blog.youkuaiyun.com/yangyang688/article/details/83011965)
void Canny( InputArray image,
OutputArray edges,
double threshold1,
double threshold2,
int apertureSize = 3,
bool L2gradient = false);
第一个参数:输入图像(八位的图像)
第二个参数:输出的边缘图像
第三个参数:下限阈值,如果像素梯度低于下限阈值,则将像素不被认为边缘
第四个参数:上限阈值,如果像素梯度高于上限阈值,则将像素被认为是边缘(建议上限是下限的2倍或者3倍)
第五个参数:为Sobel()运算提供内核大小,默认值为3
第六个参数:计算图像梯度幅值的标志,默认值为false
3、OpenCV 中,可在图像的边缘检测之后,使用 findContours 寻找到轮廓(https://blog.youkuaiyun.com/mangobar/article/details/79746339)
canny算子之后使用findcontour
六、数据格式转换(OpenCV->Halcon)
1、OpenCV数据格式转换成Halcon数据格式HObject(https://blog.youkuaiyun.com/eric_e/article/details/79350260)
2、OpenCV的Mat和Halcon的HObject类型互相转换(https://blog.youkuaiyun.com/c1learning/article/details/89510753)
七、vc++联合halcon
1、VS(C++)配置Halcon(一次配置,永久使用)(https://www.cnblogs.com/xixixing/p/10780530.html)
四步:
step1:
VC++目录,包含目录添加:
C:\Program Files\MVTec\HALCON-18.11-Progress\include\halconcpp
C:\Program Files\MVTec\HALCON-18.11-Progress\include
step2:
VC++目录,库目录添加:
C:\Program Files\MVTec\HALCON-18.11-Progress\lib\x64-win64
step3:
链接器\常规,附加库目录添加:
C:\Program Files\MVTec\HALCON-18.11-Progress\lib\x64-win64
step4:
连接器\输入,附加依赖项输入:
halconcpp.lib
出现错误:无法打开文件"msvcprt.lib"
解决办法:
接着又出现错误
问题:由于找不到halconcpp.dll,无法继续执行代码,重新安装程序可能会解决此问题。
解决方法:将halconcpp.dll放在项目的运行目录下,明明已经在库目录加过路径了,不知道为什么没作用。
/*
此文件用于将Mat数据格式转换为HImage格式,并处理
反之同样可以
但是我的电脑转换效率较慢,大概需要600多ms
*/
#include <HalconCpp.h>
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace HalconCpp;
using namespace cv;
HObject Mat2HObject(const cv::Mat &image)
{
HObject Hobj = HObject();
int hgt = image.rows;
int wid = image.cols;
int i;
// CV_8UC3
if (image.type() == CV_8UC3)
{
vector<cv::Mat> imgchannel;
split(image, imgchannel);
cv::Mat imgB = imgchannel[0];
cv::Mat imgG = imgchannel[1];
cv::Mat imgR = imgchannel[2];
uchar* dataR = new uchar[hgt*wid];
uchar* dataG = new uchar[hgt*wid];
uchar* dataB = new uchar[hgt*wid];
for (i = 0; i<hgt; i++)
{
memcpy(dataR + wid*i, imgR.data + imgR.step*i, wid);
memcpy(dataG + wid*i, imgG.data + imgG.step*i, wid);
memcpy(dataB + wid*i, imgB.data + imgB.step*i, wid);
}
GenImage3(&Hobj, "byte", wid, hgt, (Hlong)dataR, (Hlong)dataG, (Hlong)dataB);
delete[]dataR;
delete[]dataG;
delete[]dataB;
dataR = NULL;
dataG = NULL;
dataB = NULL;
}
// CV_8UCU1
else if (image.type() == CV_8UC1)
{
uchar* data = new uchar[hgt*wid];
for (i = 0; i<hgt; i++)
memcpy(data + wid*i, image.data + image.step*i, wid);
GenImage1(&Hobj, "byte", wid, hgt, (Hlong)data);
delete[] data;
data = NULL;
}
return Hobj;
}
cv::Mat HObject2Mat(HObject Hobj)
{
HTuple htCh;
HString cType;
cv::Mat Image;
ConvertImageType(Hobj, &Hobj, "byte");
CountChannels(Hobj, &htCh);
Hlong wid = 0;
Hlong hgt = 0;
if (htCh[0].I() == 1)
{
HImage hImg(Hobj);
void *ptr = hImg.GetImagePointer1(&cType, &wid, &hgt);//GetImagePointer1(Hobj, &ptr, &cType, &wid, &hgt);
int W = wid;
int H = hgt;
Image.create(H, W, CV_8UC1);
unsigned char *pdata = static_cast<unsigned char *>(ptr);
memcpy(Image.data, pdata, W*H);
}
else if (htCh[0].I() == 3)
{
void *Rptr;
void *Gptr;
void *Bptr;
HImage hImg(Hobj);
hImg.GetImagePointer3(&Rptr, &Gptr, &Bptr, &cType, &wid, &hgt);
int W = wid;
int H = hgt;
Image.create(H, W, CV_8UC3);
vector<cv::Mat> VecM(3);
VecM[0].create(H, W, CV_8UC1);
VecM[1].create(H, W, CV_8UC1);
VecM[2].create(H, W, CV_8UC1);
unsigned char *R = (unsigned char *)Rptr;
unsigned char *G = (unsigned char *)Gptr;
unsigned char *B = (unsigned char *)Bptr;
memcpy(VecM[2].data, R, W*H);
memcpy(VecM[1].data, G, W*H);
memcpy(VecM[0].data, B, W*H);
cv::merge(VecM, Image);
}
return Image;
}
int main()
{
// OpenCV读取图片
cv::Mat img = cv::imread("ROI1.jpg", 0);
HImage Mandrill = Mat2HObject(img);
// HImage Mandrill("ROI1");
Hlong width, height;
Mandrill.GetImageSize(&width, &height);
HWindow w(0, 0, width, height);
Mandrill.DispImage(w);
w.Click();
w.ClearWindow();
HRegion Bright = Mandrill >= 128;
HRegion Conn = Bright.Connection();
HRegion Large = Conn.SelectShape("area", "and", 500, 90000);
HRegion Eyes = Large.SelectShape("anisometry", "and", 1, 1.7);
Eyes.DispRegion(w);
w.Click();
}
C库函数memcpy()
C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字符到存储区 str1。
void *memcpy(void *str1, const void *str2, size_t n)
- str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
- str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。
- n -- 要被复制的字节数。
上述数据转换代码中,image.step表示每行的字节数。
Reference:
1、C库函数memcpy函数说明(https://www.runoob.com/cprogramming/c-function-memcpy.htm)
2、Opencv Mat矩阵中data、size等属性的理解(https://blog.youkuaiyun.com/daaizjh/article/details/81053165 )
七、使用halcon处理深度图
1、 思路,搞一个measure_handle,划定一个长方形矩形,在measure_handle里测量直线上的灰度值,并通过plot_funct_1d画出来。
2、小知识记录:halcon中弧度角度转换
halcon中弧度和角度的转换关系如下
1弧度=180/Π度
1度=Π/180弧度
radians to degress deg(a) 弧度转角度
degress to radians rad(a) 角度转弧度
Ref:
1、Halcon 1D测量(2) :测量特定灰度值像素(https://blog.youkuaiyun.com/fred_yang2013/article/details/12253589)
2、halcon算子翻译——measure_projection (https://www.cnblogs.com/xhiong/p/measure_projection.html)
八、使用halcon边缘提取与拟合
1、halcon例程->方法-> 边缘提取(亚像素精度)->distance_pc.hedev
该例程主要讲解 distance_pc,可以求得拟合圆中最大和最小的拟合圆的半径
2、halcon例程->方法-> 边缘提取(亚像素精度)->circles.hdev
该例程通过get_contour_global_attrib_xld算子获得所选轮廓的属性,根据属性拟合圆。