基于Qt、opencv的规则工件尺寸识别

第一步:打开摄像头

void MainWindow::on_OpenCameraBtn_clicked()
{
    capture = cvCreateCameraCapture(0);//打开摄像头,从摄像头中获取视频
    if(capture==NULL)
    {
        qDebug()<<"error!";
    }
    timer->start(50);//开始计时,超时则发出timeout()信号,1000为1秒,50毫秒取一帧
}

其中cvCreateCameraCapture()函数中参数表示为摄像头的ID,0表示使用任意一个摄像头,如果电脑有多个摄像头,还需指定ID,返回capture指针,这使得后面可以用从视频流获取帧的办法来处理图像。

第二步:获取图像

    frame = cvQueryFrame(capture);//从摄像头中抓取并返回每一帧
    // 将抓取到的帧,从IplImage格式转换为QImage格式,rgbSwapped是为了显示效果色彩好一些
    //QImage::Format_RGB888不同的摄像头用不同的格式。
   QImage image = QImage((const uchar*)frame->imageData,
                         frame->width, frame->height,
                         QImage::Format_RGB888).rgbSwapped();

此处获取的图像为Mat类型,但是为了显示效果更好,使用rebSwapped()函数,转换为QImage类型,后边黑白二值化后要转换回Mat类型,因为opencv视觉库是对Mat类型的图像操作,或者此处不用转换,灰度处理和黑白二值化都用opencv中的函数。

第三步:灰度处理


   for (int i = 0; i < image.width(); i++)
      {
          for (int j= 0; j < image.height(); j++)
          {
              QRgb color = image.pixel(i, j);
              int gray = qGray(color);

              image.setPixel(i, j, qRgba(gray, gray, gray, qAlpha(color)));
          }
      }

              ui->label_3->setPixmap(QPixmap::fromImage(image));

灰度处理我并没用用opencv库,而是用的加权平均法,符合人眼对颜色的感知,对绿色感知强,对蓝色感知弱,F(i,j) = 0.30R(i,j) + 0.59G(i,j) + 0.11B(i,j)),循环处理每个像素点就可以了。若是使用opencv直接对Mat型图像操作,应该使用函数cvtColor(srcImage,dstImage,CV_BGR2GRAY);

第三步:黑白二值化

   for (int i = 0; i < image.width(); i++)
      {
          for (int j= 0; j < image.height(); j++)
          {
              QRgb color = image.pixel(i, j);
              int gray = qGray(color);

              if(gray<120)
              {
                  gray=0;
              }
              else
              {
                  gray=255;
              }

              image.setPixel(i, j, qRgba(gray, gray, gray, qAlpha(color)));
          }
      }

转化为二值化图像代码和灰度处理差不多,只是在循环每个像素点的时候加了个判断,低于阈值归零为纯黑色,高于阈值归255为纯白色。

第四步:滤波

         cv::Mat result1;
         cv::Mat image1;


         image1 = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.bits(), image.bytesPerLine());
         cv::cvtColor(image1, image1, CV_BGR2RGB);

         //中值滤波
         cv::Mat medianFilter;
         cv::Mat medianFilter1;
         cv::medianBlur (image1,medianFilter1,3);

        //均值滤波
         cv::blur(medianFilter1,medianFilter,cv::Size(5,5));

从黑白图像到轮廓需要滤波,否则干扰很大。滤波前,需要将QImage类型的图像转化为Mat类型的图像,使用opencv中Mat()函数即刻完成转换。

我使用了两个滤波,均值滤波模糊图像,中值滤波去掉椒盐噪声,效果还算明显。

第五步:轮廓提取


    cv::Canny(medianFilter,result1,150,220);
    cv::namedWindow("Canny medianFilter");
    cv::imshow("Canny medianFilter",result1);

使用Canny函数即可完成转换。

函数说明:

第一个参数表示输入图像,必须为单通道灰度图。

第二个参数表示输出的边缘图像,为单通道黑白图。

第三个参数和第四个参数表示阈值,这二个阈值中当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割即如果一个像素的梯度大与上限值,则被认为是边缘像素,如果小于下限阈值,则被抛弃。如果该点的梯度在两者之间则当这个点与高于上限值的像素点连接时我们才保留,否则删除。

第五个参数表示Sobel 算子大小,默认为3即表示一个3*3的矩阵。Sobel 算子与高斯拉普拉斯算子都是常用的边缘算子,详细的数学原理可以查阅专业书籍。

第六步:获取周长与面积


    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    findContours( result1, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

    /// 计算矩
    vector<Moments> mu(contours.size() );
    for( unsigned int i = 0; i < contours.size(); i++ )
       { mu[i] = moments( contours[i], false ); }

    ///  计算中心矩:
    vector<Point2f> mc( contours.size() );
    for( unsigned int i = 0; i < contours.size(); i++ )
       { mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }

    cv::Mat drawing = Mat::zeros(result1.size(), CV_8UC3);                                                         //绘制轮廓
    for (unsigned int i = 0; i< contours.size(); i++)
    {
        Scalar color = Scalar(255, 0, 0);
        drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());                         //绘制外层和内层轮廓
        circle(drawing, mc[i], 4, color, -1, 8, 0);
    }
    for (unsigned int i = 0; i< contours.size(); i++)
    {
       // printf(" 通过m00计算出轮廓[%d]的面积: (M_00) = %.2f \n计算出的面积=%.2f , 长度: %.2f \n", i, mu[i].m00, contourArea(contours[i]), arcLength(contours[i], true));
              qDebug()<<"通过m00计算出轮廓[%d]的面积: (M_00)"<<mu[i].m00*0.00007194;
              qDebug()<<"计算出的面积="<<contourArea(contours[i])*0.00007194;
              qDebug()<<"长度="<<arcLength(contours[i],false)*0.002;
              S=contourArea(contours[i])*0.00013548;
              L=arcLength(contours[i],false)*0.01149425;
   //2cm圆
              if(L<6.6&&L>=6.8)
              {
                  L=L+0.4;
              }
              if(L<6.4&&L>=6.6)
              {
                  L=L-0.3;
              }
              if(L<6.4&&L>=6.2)
              {
                  L=L-0.1;
              }

              ui->lineEdit_2->setText(QString::number(S));
              ui->lineEdit->setText(QString::number(L));

        Scalar color2 = Scalar(255, 255, 0);
        drawContours(drawing, contours, i, color2, 2, 8, hierarchy, 0, Point());
        circle(drawing, mc[i], 4, color2, -1, 8, 0);
    }

opencv函数,直接上手使用,其中我为了更使结果更准确,加了一个2cm圆趋近。

第七步:关闭摄像头

void MainWindow::on_CloseCameraBtn_clicked()
{
    timer->stop(); //停止取帧
    cvReleaseCapture(&capture); //释放资源
}

效果图:

总结:1.该软件代码大部分使用opencv视觉库,所有opencv库一定要安装正确,保证函数都能调用,opencv库功能强大,学会使用并了解其中功能的原理会加深对算法的认识,这次看opencv库中的函数原理,基本都是根据数学知识得出来的,所有学好高数还是很有必要的,某宝有卖讲解opencv的书。有时间(有钱)要买一本了解一下。

2.识别误差主要来源与摄像头与平面不平行,导致检测到的图像与实物的二维图像不符,也有其他的方法进行尺寸识别,这次参加比赛有位老师跟我聊药品尺寸测量,工业中药品的尺寸测量精度已经可以达到了百分之99多。测量尝试使用摄像机,能够增加测量精度,还可以用热成像技术测量尺寸。所以1.不能偏安一隅,要多去了解各类产品,开阔眼界。2.做东西要有针对性,把实物的各个性能协调好,要将实物最关键的地方做到行业的中上水平。

猜你喜欢

转载自blog.csdn.net/duidaifen3896/article/details/83473625