毕设问题汇总

版权声明:本文为博主原创文章,未经博主允许不得转载。如有问题,欢迎指正。 https://blog.csdn.net/weixin_43197380/article/details/89710148

        我毕业设计做的是芯片引脚缺陷检测,用的是halcon+MFC,其实说白了,就是将halcon的程序进行导出、拆分,嵌入到mfc各个部件中,从而实现整体功能。因为研一上学期学的其实是opencv+qt5,所以这部分不是很熟。这里主要将我做的毕设的过程,以及在这过程中遇到的问题记录下来,留作以后参考,也算是经验的积累。

描述一:在检测引脚平面度的时候,需要将引脚底部中心点画出来,可利用halcon助手的基于互相关的模板匹配例子,导出后可拆分成打开原图、设定模板、匹配图三个部分,但在第三个匹配图中我想直接把halcon里面的关于引导中心点十字光标显示出来,如图:
在这里插入图片描述
        但这部分程序(如下)只有输出图(参数一),各中心的坐标(hv_MatchingCol为x坐标, hv_MatchingRow为y坐标),但是没有句柄,自己创建一个句柄有错误,不知道可不可以直接显示输出图(有大神知道的可以告知一下,感激不尽)。

GenCrossContourXld(&ho_TransContours, hv_MatchingRow, hv_MatchingCol, 20, hv_MatchingAngle);

最后没有办法,只能通过上边的坐标直接通过画笔挨个把点画出来。

问题一:如何获取引脚中心点的坐标?

解决:

上边的GenCrossContourXld()函数给出了 HTuple类型的中心点行列坐标,需要将HTuple类型转换成int/double类型的。

问题二:如何把坐标点画到图片上(画笔、画刷的用法),而不是图片所在的控件上?

解决:
为你图片所在的控件添加一个变量,比如m_pic,然后在OnPaint()中加入

//调用实现图片打开功能的函数
OnBnClickedButton2();
//例:在图片上画一个圆
CDC *pDC = m_pic.GetWindowDC();
CPen pen(PS_SOLID,6,RGB(255,0,0));
pDC->SelectObject(&pen);
pDC->Ellipse(50,50,101,101);

描述二:当时可以将点画在图片上面了,但是当点比较大的时候,需要将图片所在的控件放大,才能显示在图片上,所以我猜测坐标可能是相对于图片的像素大小而言的,也就是像素坐标,要想在mfc控件上显示准确的坐标,一种方法是将控件放大到和图片像素大小一下(显然不靠谱),而另一种方法就需要将坐标进行一个缩放了。

问题三:如何将相对于图片像素大小的坐标,进行缩放,准确显示到Picture Control上的图片上?

解决:
当不进行缩放时,在原始图片上显示的点坐标,
在这里插入图片描述
到了mfc对话框中,可能会显示不出来(控件太小了),将控件放大后,可能会在任何位置显示,不会准确显示到引脚中心,所以需要将坐标进行缩放,依据的原理是:
在这里插入图片描述
只需要将相对于图片大小的坐标缩放到相对于控件大小的即可,可通过公式控件宽/图片宽=x“/x=y”/y, 即可求出缩放后的坐标(x“,y”),而图片宽,控件宽能求,(x,y)已知。
代码:

	//获取控件尺寸和位置
	CRect rectCtrl;
	CStatic *p = (CStatic*)GetDlgItem(IDC_STATIC3);
	//p->MoveWindow(100, 100, 100, 100);//更改控件大小并移动其到指定位置
	p->GetWindowRect(rectCtrl);
	this->ScreenToClient(rectCtrl);
	//GetDlgItem(IDC_STATIC_TEST)->GetClientRect(rectCtrl);
	int width =  rectCtrl.Width();
	int height =  rectCtrl.Height();

	OnBnClickedButton1();
	//获取图片尺寸(pic为图片的类型)
    int cx = pic.GetWidth();
    int cy = pic.GetHeight();

	OnBnClickedButton2();
	CDC *pDC = m_pic.GetWindowDC();
	CPen pen(PS_SOLID,4,RGB(255,0,0));
	pDC->SelectObject(&pen);
	//point.x ==col
	//point.y ==row
	int i=0;
	int j=0;
	for(;i<5,j<5;i++,j++)
	{
	double ti = hv_MatchingCol[i].D(); 
	double tj = hv_MatchingRow[j].D();
	pDC->Ellipse((ti/cx)*width,(tj/cy)*height,(ti/cx)*width+1,(tj/cy)*height+1);
	}

效果:
在这里插入图片描述

问题四:c++ 如何将多个点存入容器?

描述:我说的将多个点存入容器,不是自己手动一个点一个点的存,那样感觉很蠢,而且换一张和之前点数不一样的图片后就不行了,不具有普遍性。

解决:
首先定义一个结构:

struct Point
 {
    double x;
    double y;
 };

然后,没改之前的程序是下面这样的,

vector<Point> points;
Point point1;
for(int i = 0; i<5;i++)
{
	point1.x=hv_MatchingCol[i].D();
	point1.y=hv_MatchingRow[i].D();
	points.push_back(point[i]);
}

        因为point1.x,point1.y的值在不断更新,这就导致容器points里面存入的值不断被新进来的值给替换掉,而不是都存入容器points中。
        解决办法就是point.x与hv_MatchingCol[i].D(),point.y与hv_MatchingRow[i].D();的值一一对应,而不是前面的值一直在更新。

改进之后的程序:

vector<Point> points;
Point point[5];
for(int i = 0; i<5;i++)
{
	point[i].x=hv_MatchingCol[i].D();
	point[i].y=hv_MatchingRow[i].D();
	points.push_back(point[i]);
}

注:halcon 里面HTuple类型的 hv_MatchingCol,hv_MatchingCol里面之前就各有五个数,而hv_MatchingCol[i].D()是将HTuple类型转换为double类型。

问题五:将多个点拟合成一条直线,并求出点到直线的最大距离和最小距离。
参考:https://blog.csdn.net/liyuanbhu/article/details/50866802

描述:

  • 最小二乘法直线拟合(不是常见的一元线性回归算法)
  • 将离散点拟合为 a x + b y + c = 0 型直线
  • 假设每个点的 X Y 坐标的误差都是符合 0 均值的正态分布的。
  • 与一元线性回归算法的区别:一元线性回归算法假定 X 是无误差的,只有 Y 有误差。

解决:
可以与上面存入点的容器对接,利用已知点,求出直线方程中的系数a,b,c。

    double a, b, c;
	int size = points.size();
    if(size < 2)
     {
         a = 0;
         b = 0;
         c = 0;

     }
     double x_mean = 0;
     double y_mean = 0;

     for(int i = 0; i < size; i++)
     {
         x_mean += points[i].x;
         y_mean += points[i].y;
     }
     x_mean /= size;
     y_mean /= size; //至此,计算出了 x y 的均值

     double Dxx = 0, Dxy = 0, Dyy = 0;

     for(int i = 0; i < size; i++)
     {
         Dxx += (points[i].x - x_mean) * (points[i].x - x_mean);
         Dxy += (points[i].x - x_mean) * (points[i].y - y_mean);
         Dyy += (points[i].y - y_mean) * (points[i].y - y_mean);
     }

     double lambda = ( (Dxx + Dyy) - sqrt( (Dxx - Dyy) * (Dxx - Dyy) + 4 * Dxy * Dxy) ) / 2.0;
     double den = sqrt( Dxy * Dxy + (lambda - Dxx) * (lambda - Dxx) );

     if(fabs(den) < 1e-5)
     {
         if( fabs(Dxx / Dyy - 1) < 1e-5) //这时没有一个特殊的直线方向,无法拟合
         {

         }
         else
         {
             a = 1;
             b = 0;
             c = - x_mean;
         }
     }
     else
     {
         a = Dxy / den;
         b = (lambda - Dxx) / den;
         c = - a * x_mean - b * y_mean;
     }

	CDC *pDC1 = m_pic.GetWindowDC();
	CPen pen1(PS_SOLID,1,RGB(0,0,255));
	pDC1->SelectObject(&pen1);
	pDC1->MoveTo((points.at(0).x)/cx*width,-((a*points.at(0).x+c)/b)/cy*height);
	pDC1->LineTo((points.at(4).x)/cx*width,-((a*points.at(4).x+c)/b)/cy*height);

效果:
在这里插入图片描述
求出点到直线的最大距离和最小距离:

    //求出引脚中心点到标注直线的距离,并存入容器dis中
	vector<double>dis;
	for(int i =0;i<size;i++)
	{
	double d1 = a * points.at(i).x+b * points.at(i).y+c;
	double d2 = sqrt(a*a+b*b);
	double d = sqrt(d1/d2*d1/d2);
	dis.push_back(d);
	}
	//求出距离最大值,最小值
	double min,max;
	max = min = dis.at(0);
	for (int i = 0; i < size; i++)
	{	
		if (max < dis.at(i))
			max = dis.at(i);
		if (min > dis.at(i))
			min = dis.at(i);	
	}

问题六:在MFC中Edit控件上显示含小数点的数,并可以控制小数点后的位数。

以double类型小数0.274002149218631为例

解决:
方法一:精确到小数点后一位
1)首先为控件Edit添加一个CString类型的变量
在这里插入图片描述
2)添加代码:

	CString s;
	s.Format("%.lg", max); //显示一位小数
	max_edit =s;
	UpdateData(FALSE);

效果:
在这里插入图片描述
方法二:原来多少位的小数,就显示多少位的:
1)为编辑框Edit添加一个double类型的变量min_edit
2)添加代码:

	min_edit = min;
    UpdateData(FALSE);//把控件关联的变量值“刷到”控件上显示

效果:
在这里插入图片描述

方法三:任意控制小数点后的位数:
1)为编辑框Edit添加一个double类型的变量min_edit
2)添加代码:

	min_edit = max;
	min_edit = floor(min_edit * 1000.000f + 0.5) / 1000.000f;
    UpdateData(FALSE);//把控件关联的变量值“刷到”控件上显示

上面是精确到小数点后三位,如果精确到小数点后两位,改成100.000f。

效果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43197380/article/details/89710148
今日推荐