条件:opencv 4.5 、Qt5.13 、DroidCam 6.0
操作系统:ubuntu 20
硬件: 电脑、手机
待测物:硬币
一、搭建交互界面
1、首先从图像源上可以分成是本地图片、相机成像(内设、外设),因此这一步设计三个图像源获取方式;分别是笔记本前置摄像头、外置摄像头(手机通过DroidCam)。
这个直接设计为下拉选项框(QComboBox),然后通过获取选项index来读取不同输入源。
2、开始按钮
直接ui上添加QPushButton,然后修改textlabel为“开始”,没有其他骚操作。
3、显示所读取的图像以及处理后的图像
这里使用两个Qlabel进行图像的显示。
4、测试结果的显示
这里主要有测试数据的显示和测试结果是否合格的显示,运用Qlable显示pass/fail的图片,以及运用QlineEdit显示测试数据。
由于本项目与其他项目共有一个ui,因此会有一些其他的控件在主界面中。
二、代码部分
1、下拉框初始化、以及信号量的初始化,
QStringList cameraList = {"前置摄像头","外置摄像头","本地图片"};
ui->cBox_camera->addItems(cameraList);
hasObject = false;
2、图像源的选择
connect(ui->cBox_camera,QOverload<int>::of(&QComboBox::activated),this,[=](int index){
ImageSource = index;//图像源标记
if(index == 2){
ui->label_filepath->show();
ui->lineEdit_filepath->show();
ui->btn_file->show();
}
else{
FilePathInit();
}
});
//输入图像切换
3、点击开始按钮后执行的函数
connect(ui->btn_start,&QPushButton::clicked,this,[&]()mutable{
working = true;//工作状态设为true,当点击停止按钮时切换false
cv::Mat src,dst;
if(ImageSource==2){//以读取本地图片作为输入图像
QString Imgpath = ui->lineEdit_filepath->text();
qDebug() << Imgpath << endl;
if(Imgpath.isEmpty()){
DemindMessage("Error","The FilePath Is Invalid\n");//文件路径为空时进行信息提醒
}
else{
src = cv::imread(Imgpath.toStdString());
cv::imshow("pic",src);
SrcImageDisplay(src);
if(CurrentStatus==0){
DefaultPro(src,dst);//自定义默认模式下处理函数
}
else if(CurrentStatus == 1){
MeasurePro(src,dst);//自定义处理函数
}
else{
DetectPro(src,dst);//自定义处理函数
}
}
MatToQImage(dst);//自定义图像类型Mat To QPixel转换函数
}
else{//以读取视频设备作为输入图像
int tmp;//因为设备号只有1、2可用,这里转换一下
if(ImageSource==0){
tmp = 1;
}
else{
tmp = 2;
}
cv::VideoCapture cap(tmp);//读取相机图像
while(cap.read(src)){
if(cv::waitKey(20)==27||!working){
break;
}
if(CurrentStatus==0){
cv::waitKey(500);//这里延时0.5s,
}
//SrcImageDisplay(src);
LabelToImage(src,ui->label_src);
if(CurrentStatus==0){
std::cout << "default" << std::endl;
DefaultPro(src,dst);
}
else if(CurrentStatus == 1){
MeasurePro(src,dst);
}
else{
DetectPro(src,dst);
}
LabelToImage(dst,ui->label_dst);
}
cap.release();
}
src.release();
dst.release();
});//启动
std::vector<float> rad;//储存物体直徑
CircleImagePro(src,dst,rad);
if(!hasObject){
return;
}//若未检测到目标则返回
for(size_t i=0;i<rad.size();++i){
if(rad[i]>float(21.50)||rad[i]<float(19.50)){//判断是否合格
ui->label_result->setStyleSheet("image:url(:/img/fail.jpeg)");
}
else{
ui->label_result->setStyleSheet("image:url(:/img/pass.jpeg)");
}
}
目标检测代码(圆形):
using namespace cv;
using namespace std;
QStringList TestData;
src.copyTo(dst);
float c = float(20.5)/float(68.9);
vector< vector<cv::Point> > contours;
//图像预处理
Mat img;
cvtColor(src,img,COLOR_BGR2GRAY);
GaussianBlur(img,img,cv::Size(3,3),1);
Canny(img,img,50,150,3);
Mat kernel = getStructuringElement(MORPH_RECT,Size(3,3));//获取卷积核
dilate(img,img,kernel,cv::Point(-1,-1),3);
erode(img,img,kernel,Point(-1,-1),2);
findContours(img,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
img.release();
vector< cv::Point2f > center(contours.size());
vector<float> rad(contours.size());
if(contours.size() == 0){
NotFoundObject(src,dst);
ui->textEdit_testdata->setText("未在图像中发现检测物!请重新检测。");
return;
}
else{
hasObject = true;
}
for(size_t i =0;i<contours.size();++i){
minEnclosingCircle(contours[i],center[i],rad[i]);
radius.push_back(rad[i]*c);
}
for(size_t j=0;j<center.size();++j){
string str = "radius: ";
string str1;
float r = 0.0;
circle(dst,center[j],rad[j],Scalar(0,0,255),1);
r = rad[j]*c;
str1 = format("%.4f",r);
str.insert(str.end(),str1.begin(),str1.end());
TestData.append(QString::fromStdString(str));
putText(dst,str,center[j],1,2,Scalar(0,255,0),2,8);
}
QStringTextOutput(TestData);
TestData.clear();
硬币测量