**考核任务总结 ——基于的形状识别任务总结 前言
? 在的初学之旅中,我深感这个图像处理库的简洁和灵活性,使我能够迅速上手,享受编写高效 图像处理代码的乐趣 。其强大功能从基本的图像操作到复杂的计算机视觉任务无所不包,让我逐渐理解 了图像处理的核心概念,并能够熟练运用各种处理技术 。开源的特性为我提供了丰富的学习资源,与庞大的开源社区互动让我受益匪浅 。通过实践编写代码,我深刻体会到理论知识的实际应用,而 提供的示例代码和文档以及为我的实践提供了坚实的基础 。
一、主要设计思路 通过摄像头或选择图像文件的方式获取输入图像 。对输入图像进行初步处理,包括颜色空间转换、滤波、边缘检测等操作,得到处理后的图像 。使用轮廓检测函数来获取图像中的轮廓 。对每个轮廓进行处理和分析,包括计算面积、周长、圆度等属性,并通过拟合多边形和凸包检测来判断形状类型 。根据形状类型进行相应的处理,如绘制轮廓、截取形状、检测颜色等 。在图像上标注形状名称和颜色信息,并显示处理后的图像 。二、技术要点和代码实现 1.导入头文件等基本操作
#include #include #include using namespace cv;using namespace std;
2.函数定义 (1)定义列表
//打开文件模块头文件string openFile(const wchar_t* windowTitle){}
//临边长比过滤函数【多边形approx,多边形边数,临边比上限,临边比下限】bool SLengthDetect(vectorapprox, int sides, double RatioUpper, double RatioLower){}
//图形颜色判断函数【返回形状名称】string judgeColor(int hue){}
//颜色检测并标注的函数(需要标注的图像,标注基准点,滤波图)【返回图形hue值】int detectColor(Mat imgRead, Point center, Mat midblurcolor){}
//截取形状图片函数 (形状的轮廓,传入图像,形状名称,编号)void CatchShape(vector approx, Mat imageRead, string shape, int id){}
//非摄像机图像初步处理函数Mat ProcessImg(Mat imgRead){}
//摄像机图像初步处理函数Mat CamProcessingImg(Mat img)
//形状识别相关函数(传入图像,是否截取形状,是否复刻原图,是否检测颜色,是否进入摄像机模式)int reconizeShape(Mat imgRead, bool is_CATCHSHAPE, bool is_COPY, bool is_DETECTCOLOR, bool is_CameraMode){}
(2)函数具体定义
//打开文件模块头文件string openFile(const wchar_t* windowTitle){OPENFILENAME ofn;wchar_t szFile[260] = { 0 };ZeroMemory(&ofn, sizeof(ofn));ofn.lStructSize = sizeof(ofn);ofn.lpstrFile = szFile;ofn.nMaxFile = sizeof(szFile);ofn.lpstrTitle = windowTitle; // 使用传入的窗口名称ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;ofn.lpstrFilter = L"Image Files\0*.bmp;*.jpg;*.png;*.JPEG;*.JPE;\0";if (!GetOpenFileName(&ofn)){wcout << L"没有选择到文件 。" << endl;return "";}int size = WideCharToMultiByte(CP_UTF8, 0, ofn.lpstrFile, -1, nullptr, 0, nullptr, nullptr);std::string selectedFile(size, 0);WideCharToMultiByte(CP_UTF8, 0, ofn.lpstrFile, -1, &selectedFile[0], size, nullptr, nullptr);return selectedFile;}//临边长比过滤函数【多边形approx,多边形边数,临边比上限,临边比下限】bool SLengthDetect(vectorapprox, int sides, double RatioUpper, double RatioLower){bool valid_shape = true;vector edge_lengths;for (size_t i = 0; i < sides; i++) {Point v = approx[i] - approx[(i + 1) % sides];double length = norm(v);edge_lengths.push_back(length);}for (size_t i = 0; i < sides; i++) {double ratio = edge_lengths[i] / edge_lengths[(i + 1) % sides];if (ratio < RatioLower || ratio > RatioUpper) {valid_shape = false;break;}}return valid_shape;}//图形颜色判断函数【返回形状名称】string judgeColor(int hue){// 判断红色if ((hue >= 0 && hue <= 15)){return "Red";}// 判断橙色if (hue > 15 && hue <= 20){return "Orange";}// 判断黄色if (hue > 20 && hue <= 45){return "Yellow";}// 判断绿色if (hue > 45 && hue <= 90){return "Green";}// 判断蓝色if (hue > 90 && hue <= 115){return "Blue";}// 判断紫色if (hue > 115 && hue <= 175){return "Purple";}return "Unknown color";}//颜色检测并标注的函数(需要标注的图像,标注基准点,滤波图)【返回图形hue值】int detectColor(Mat imgRead, Point center, Mat midblurcolor){//检测中心点颜色Rect regionOfInterest((center.x) - 5, (center.y) - 5, 10, 10); //取样Mat roi = midblurcolor(regionOfInterest);// 创建一个副本,仅包含指定区域的图像数据int totalHue = 0;int pixelCount = 0;// 初始化Hue总和和像素计数器// 遍历区域内的每个像素并计算Hue总和for (int y = 0; y < roi.rows; y++) {for (int x = 0; x < roi.cols; x++) {Vec3b pixel = roi.at(y, x);Mat3b rgb(pixel);Mat3b hsv;cvtColor(rgb, hsv, COLOR_BGR2HSV);int hue = hsv(0, 0)[0];totalHue += hue;pixelCount++;}}// 计算Hue的平均值int averageHue = totalHue / pixelCount;string centerColor = judgeColor(averageHue);Point olcenter(center.x, center.y + 30);putText(imgRead, centerColor, olcenter, FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 0, 255), 1);return averageHue;}//截取形状图片函数 (形状的轮廓,传入图像,形状名称,编号)void CatchShape(vector approx, Mat imageRead, string shape, int id){//分别截取形状Rect boundingBox = boundingRect(approx);Mat CatC = imageRead(boundingBox);bool saved = imwrite(shape + to_string(id) + ".jpg", CatC, { IMWRITE_JPEG_QUALITY, 90 });}//摄像机图像初步处理函数Mat CamProcessingImg(Mat img){// 转换为HSV颜色空间Mat HSV;cvtColor(img, HSV, COLOR_BGR2HSV);// 定义白色的饱和度阈值Scalar lower(0, 35, 0);Scalar upper(179, 255, 255);// 创建一个掩码来过滤背景Mat mask;inRange(HSV, lower, upper, mask);// 高斯模糊Mat blur;GaussianBlur(mask, blur, Size(5, 5), 1);// 使用Canny算子提取边缘Mat canny;Canny(blur, canny, 50, 50);// 一次闭运算,去除杂点Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));Mat mask1;Mat mask2;dilate(canny, mask1, kernel, Point(-1, -1), 1);erode(mask1, mask2, kernel, Point(-1, -1), 1);return mask2;}//非摄像机图像初步处理函数Mat ProcessImg(Mat imgRead){// 灰度图像转换 Mat gray;cvtColor(imgRead, gray, COLOR_BGR2GRAY);// 中值滤波Mat midblurgray;medianBlur(gray, midblurgray, 5);// 执行高斯滤波Size kernelSize(5, 5);Mat gaussgray;GaussianBlur(midblurgray, gaussgray, kernelSize, 2.5, 2.5);//二值化处理Mat binary;threshold(gaussgray, binary, 150, 250, THRESH_BINARY_INV);// 边缘检测Mat cannygray;Canny(binary, cannygray, 100, 150);return cannygray;}
//形状识别相关函数(传入图像,是否截取形状,是否复刻原图,是否检测颜色,是否进入摄像机模式)int reconizeShape(Mat imgRead, bool is_CATCHSHAPE, bool is_COPY, bool is_DETECTCOLOR, bool is_CameraMode){// 定义颜色Scalar Rcolor(0, 0, 255); // 红色//新建复刻用画布Mat whiteimg(imgRead.size(), CV_8UC3, Scalar(255, 255, 255));//错误处理if (imgRead.empty()) {cerr << "Error: Could not load imgRead." << endl;return -1;}// 中值滤波(用于检测颜色)Mat midblurcolor;medianBlur(imgRead, midblurcolor, 15);//图像初步处理Mat cannygray;if (!is_CameraMode) { cannygray = ProcessImg(imgRead); }else { cannygray = CamProcessingImg(imgRead); }// 读取轮廓vector contours;if(is_CameraMode){ findContours(cannygray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE); }else { findContours(cannygray, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); }// 历遍每个轮廓for (size_t i = 0; i < contours.size(); ++i){//-------轮廓相关信息采集与初步过滤---------//double area = contourArea(contours[i]); // 计算面积if (area < 300) { continue; } //过滤掉面积过小的轮廓double perimeter = arcLength(contours[i], true); // 计算周长double circularity = 4 * CV_PI * area / (perimeter * perimeter); // 计算圆度// 设置一个阈值来筛选陡峭的边if (circularity < 0.2) { continue; }//--------------------------------//// 二次拟合多边形vector approx1;vector approx2;approxPolyDP(contours[i], approx1, 0.001 * arcLength(contours[i], true), true);approxPolyDP(approx1, approx2, 0.03 * arcLength(contours[i], true), true);// 获取多边形的顶点数int numVertices = approx2.size();//计算轮廓的中心Moments M = moments(contours[i]);Point center(int(M.m10 / M.m00), int(M.m01 / M.m00));// -----根据顶点数和凸包数最后一步过滤并判断形状-------//int numSides = approx2.size();string shape;if (numSides == 3){vector Tcontours;convexHull(approx2, Tcontours);//凸包检测if (Tcontours.size() == 3 && SLengthDetect(approx2, 3, 10, 0.1)){shape = "Triangle";drawContours(imgRead, contours, i, Rcolor, 1);}}else if (numSides == 4){?vector Rcontours;?convexHull(approx2, Rcontours);?if (Rcontours.size() == 4 && SLengthDetect(approx2, 4, 1.5, 0.6))?{shape = "Rectangle";?drawContours(imgRead, contours, i, Rcolor, 1);?}}else if (numSides == 5){?vector Pgcontours;?convexHull(approx2, Pgcontours);?if (Pgcontours.size() == 5 && SLengthDetect(approx2, 5, 2.5, 0.4))?{?shape = "Pentagon";?drawContours(imgRead, contours, i, Rcolor, 1);?}}else if (numSides == 6){?vector Pccontours;convexHull(approx2, Pccontours);?if (Pccontours.size() == 6 && SLengthDetect(approx2, 6, 2.5, 0.4))?{?shape = "Hexagon";?drawContours(imgRead, contours, i, Rcolor, 1);?}}else if (numSides == 10){?vector Pccontours;?convexHull(approx2, Pccontours);?if (Pccontours.size() == 5)?{?shape = "Pentacle";?drawContours(imgRead, contours, i, Rcolor, 1);?}}else if (numSides > 7){// 圆形的检测float peri1 = arcLength(contours[i], true);float apm = (peri1 * peri1) / (area * 4);if (apm > 2.4 && apm < 3.8){shape = "Circle";drawContours(imgRead, contours, i, Rcolor, 1);}}else { continue; }//----------------------------////截取形状if (is_CATCHSHAPE){CatchShape(approx2, imgRead, shape, i);}//检测颜色并标注int averageHue = 0;if (is_DETECTCOLOR){averageHue = detectColor(imgRead, center, midblurcolor);}//hue平均值转BGRMat hsvColor(1, 1, CV_8UC3, Scalar(averageHue, 255, 255));Mat rgbColor;cvtColor(hsvColor, rgbColor, COLOR_HSV2BGR);// 提取BGR颜色值Vec3b rgbPixel = rgbColor.at(0, 0);int eblue = rgbPixel[0];int egreen = rgbPixel[1];int ered = rgbPixel[2];//复刻图片中的形状if (is_COPY){drawContours(whiteimg, contours, i, Scalar(eblue, egreen, ered), -1);}//标注形状名称,画出轮廓putText(imgRead, shape, center, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0), 1);}if (is_COPY){imshow("copyimg", whiteimg);}return 0;}
int main(){int mode = 0;do{cout << "请输入对应模式的数字\n1.摄像机模式\n2.图像模式" << endl;cin >> mode;if (mode == 1){VideoCapture cap(1);if (!cap.isOpened()){cerr << "Error: Couldn't open the camera." << endl;return -1;}int frameCount = 0;auto start = chrono::high_resolution_clock::now();while (true){// 使用shared_ptr创建Mat对象,确保自动内存管理shared_ptr frame = make_shared();// 从摄像头中读取帧cap >> *frame;if (frame->empty()){cerr << "Error: Couldn't read a frame from the camera." << endl;break;}// 进行帧处理操作reconizeShape(*frame, 0, 0, 1, 1);// 计数帧frameCount++;// 计时法估算帧率auto end = chrono::high_resolution_clock::now();double elapsedTime = chrono::duration_cast(end - start).count();double fps = static_cast(frameCount) / elapsedTime;//显示帧率putText(*frame, "FPS:" + to_string(fps), Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 0), 2);imshow("imgRead", *frame);// 退出循环条件(按下'Esc'键退出)if (waitKey(1) == '27'){break;}}}else if (mode == 2){const wchar_t* WindowTitle = L"选择需要传入的图像文件";Mat ImgRead = imread(openFile(WindowTitle));if (ImgRead.empty()){cerr << "无法读取图像文件或未选取文件" << endl;return -1;}reconizeShape(ImgRead, 1, 1, 1, 0);//形状识别相关函数(传入图像,是否截取形状,是否复刻原图,是否检测颜色,是否进入摄像机模式)imshow("img", ImgRead);// 退出循环条件(按下'Esc'键退出)waitKey(0);}} while (mode != 1 && 2);waitKey(0);system("pause");return 0;}
? a. 颜色识别部分只通过色度来判断,而非整体的HSV,因此可能比较不准确 。
? b. 矩形色区(用于提取H值并识别颜色的区域)的选取或许会出现超出目标内部范围的问题 。
? c. 摄像机图像处理在复杂场景下不稳定,还需加强 。
文章插图
三、效果展示
四、遇到的问题及解决方法
? 通过学习基本的图像处理技术,如边缘检测和轮廓提取,我逐渐掌握了如何在图像中寻找形状 。这是一个让我感到满足的过程,因为我可以看到如何将一堆像素转化为有意义的信息 。
? 刚开始我还是个零基础的编程小白,但通过学习鱼实践我逐渐学会了编写有结构的代码,给变量和函数取有意义的名字,以及添加注释来解释我的代码 。这有助于我更容易理解和维护自己的程序 。
? 一开始我用霍夫圆变换来检测圆,但是霍夫圆变换不能实现多角度识别圆形,于是我通过获取圆度(周长平方/4倍面积)来检测圆;一开始不知道有凸包检测,便只通过角点个数来判断五角星,但我细想这样把十边形也检测为五角星,于是我查找是否有能检测凸角的函数,通过各方社区,终于给我找到了凸包检测 。
【**考核任务总结 ——基于opencv的形状识别任务总结】总的来说,作为一个新手,学习过程中的挑战使我更有动力不断学习和提高 。我发现计算机视觉是一个充满无限可能的领域,而是我进入这个领域的理想起点 。我期待着在未来的项目中继续探索的潜力,学到更多的知识,以及应用它在实际生活中 。