一.图片裁剪与选取部分

int main()
{
    string path = "C:\\Users\\jishu\\Pictures\\aoqi";//这里的路径是你图片的绝对路径,并且用\\来分隔每个文件
    Mat img = imread(path);
    Mat imgResize, imgGrop;
    
    resize(img, imgResize, Size(), 0.5, 0.5);

    Rect roi(27, 54, 162, 209);
    imgGrop = img(roi);

    imshow("img", img);
    imshow("img Reasize", imgResize);
    imshow("img Grop", imgGrop);
    
    waitKey(0);
    return 0;
}

resize:作用是调整图片大小,可以在size中填入你想要的大小,也可以在第四五位参数用倍数调整。

选取部分图片

先建立一个矩形roi,里面的参数分别是矩形左上角的xy像素点坐表和右下角xy的像素点坐标

在画图工具中打开图片,选取图中白色精灵圈出的小点,坐标位置在左下角。

代码效果如下

二.建立图形(矩形,圆形,线等)

int main()
{
    
    Mat img(512, 512, CV_8UC3, Scalar(255,255,0));
    circle(img, Point(256, 256), 155, Scalar(0, 0, 0), 5);
    rectangle(img, Point(130,226), Point(382,296), Scalar(255,255,255), FILLED);
    line(img, Point(130,296), Point(382,296), Scalar(0,0,0), 3);

    putText(img, "666666666666", Point(137,262), FONT_HERSHEY_COMPLEX,0.75, Scalar(0,0,0), 1);

    imshow("image", img);
    waitKey(0);
    return 0;
}

 Mat img(512, 512, CV_8UC3, Scalar(255,255,0));

创建一个名为img的Mat变量,给这个变量长宽各512,512(前两个参数)的像素,一个颜色由三种(C3)通道组成,一个通道有8个bit位(8U)组成,255种情况,颜色组成是Red+Green(255,255,0)。

circle(img, Point(256, 256), 155, Scalar(0, 0, 0), 5);

在img上作圆,原点坐标是Point(256,256),半径是155,颜色是黑(0,0,0),线粗度是5

rectangle(img, Point(130,226), Point(382,296), Scalar(255,255,255), FILLED);

在img上作矩形,两个点可以确定矩形,分别是左上角和右下角两点,颜色是白(255,255,255),FILLED表示填满(也可以是其他数字,与圆最后一个参数同样,表示线的粗度)

line(img, Point(130,296), Point(382,296), Scalar(0,0,0), 3);

在img上作线,两点(Point)确定一线,颜色是黑(0,0,0).粗度是3

putText(img, "666666666666", Point(137,262), FONT_HERSHEY_COMPLEX,0.75, Scalar(0,0,0), 1);

在img上输入文案,文案是“6666666666”,初始点是矩形左上角(137,262),FONT_HERSHEY_COMPLEX表示字体,右很多种字体,可以自行查阅,大小是0.75,颜色是黑(0,0,0),粗细是1

效果图

三.图片投影

int main()
{
    float w = 250, h = 350;
    Mat matrix, imgWarp;

    string path = "Resources/cards.jpg";
    Mat img = imread(path);

    Point2f src[4] = { {528,144}, {772,192}, {403,393}, {674,457} };
    Point2f dis[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };

    /*for (int i =0; i < 4; i++) {
        circle(img, Point(src[i]), 2, Scalar(0, 0, 0), FILLED);
    }*/
    matrix = getPerspectiveTransform(src, dis);
    warpPerspective(img, imgWarp, matrix, Point(w, h));
    imshow("Image", img);
    imshow("Img Warp", imgWarp);
    waitKey(0);
    return 0;
}

便于理解先发效果图了(这里选取扑克k)

point2f表示point表示点,2表示一个点由两个属性组成,f表示数的精度。

注释的代码是确定找的点概括到了扑克牌四个点

w,h是提前计算好的扑克牌的宽和高

 matrix = getPerspectiveTransform(src, dis);
 warpPerspective(img, imgWarp, matrix, Point(w, h));

为代码的核心函数,可以把src中斜着的图像拉正

四.颜色识别

要先讲rgb图像转成hsv图像,在特定的h,s,v的范围内(指的其实就是某种颜色的相近范围),对应的颜色会在黑色图像同样生成白色掩膜

首先,我们需要回忆起hsv图像(很重要,不懂的可以去看上一章内容)

int main()
{
    string path = "Resources/lambo.png";
    Mat img = imread(path);
    Mat imgHSV, mask;
    int hmin=0, smin=110, vmin=153;
    int hmax=19, smax=240, vmax=255;
    cvtColor(img, imgHSV, COLOR_BGR2HSV);
    namedWindow("Trackbars",(640,200));
    createTrackbar("Hue Min", "Trackbars", &hmin, 179);
    createTrackbar("Hue Max", "Trackbars", &hmax, 179);
    createTrackbar("Sat Min", "Trackbars", &smin, 255);
    createTrackbar("Sat Max", "Trackbars", &smax, 255);
    createTrackbar("Val Min", "Trackbars", &vmin, 255);
    createTrackbar("Val Max", "Trackbars", &vmax, 255);
    while (true)
    {
        Scalar lower(hmin, smin,vmin);
        Scalar upper(hmax, smax, vmax);
        inRange(imgHSV, lower, upper, mask);
        imshow("Image", img);
        imshow("Image HSV", imgHSV);
        imshow("MASK", mask);
        waitKey(1);
    }
    return 0;
}

 cvtColor(img, imgHSV, COLOR_BGR2HSV);

讲原来的图像转为hsv图像

Scalar lower(hmin, smin,vmin);
Scalar upper(hmax, smax, vmax);

分别生成lower和upper两种颜色(Scalar表示声明创建的变量的是颜色)

展示一下效果,方便理解

int hmin=0, smin=110, vmin=153;
int hmax=19, smax=240, vmax=255;

表示检测的是图中汽车车身的颜色,是一个范围

namedWindow("Trackbars",(640,200));
    createTrackbar("Hue Min", "Trackbars", &hmin, 179);
    createTrackbar("Hue Max", "Trackbars", &hmax, 179);
    createTrackbar("Sat Min", "Trackbars", &smin, 255);
    createTrackbar("Sat Max", "Trackbars", &smax, 255);
    createTrackbar("Val Min", "Trackbars", &vmin, 255);
    createTrackbar("Val Max", "Trackbars", &vmax, 255);

创建名为"Trackbars"的一个窗口,大小是(640,200)

createTrackbar表示在第二个参数"Trackbars"的窗口生成一个名为"Hue Min"轨道条,拖动轨道可以改变&hmin这个地址中的值(以第一个举列)

五.轮廓检测

void getContours(Mat imgDil, Mat img)
{
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

    vector<vector<Point>> conPoly(contours.size());
    vector<Rect> boundRect(contours.size());

    for (int i = 0; i < contours.size(); i++)
    {
        string objectType;
        int area = contourArea(contours[i]);
        cout << area << endl;

        if (area > 1000)
        {
            float peri = arcLength(contours[i], true);
            approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
            cout << conPoly[i].size() << endl;
            //drawContours(img, contours, -1, Scalar(255, 0, 255), 2);
            boundRect[i] = boundingRect(conPoly[i]);
            int objCor = (int)conPoly[i].size();
            if (objCor == 3)
            {
                objectType = "Tri";
            }
            else if (objCor == 4)
            {
                float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
                if (aspRatio <= 1.05 && aspRatio >= 0.95)
                {
                    objectType = "Square";
                }
                else {
                    objectType = "Rect";
                }
                
            }
            else if (objCor > 4)
            {
                objectType = "Circle";
            }
            drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);

            rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(255,0,255), 2);
            putText(img, objectType, { boundRect[i].x, boundRect[i].y-5 }, FONT_HERSHEY_COMPLEX,
            0.5, Scalar(0,0,0), 1);
        }

    }


}

int main()
{
    string path = "Resources/shapes.png";//这里的路径是你图片的绝对路径,并且用\\来分隔每个文件
    Mat img = imread(path);
    Mat imgGray, imgBlur, imgCanny, imgDil, imgEro;

    cvtColor(img, imgGray, COLOR_BGR2GRAY);
    GaussianBlur(img, imgBlur, Size(3, 3), 3, 0);
    Canny(imgBlur, imgCanny, 25, 75);
    Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
    dilate(imgCanny, imgDil, kernel);
    erode(imgDil, imgEro , kernel);//预处理
    getContours(imgDil, img);
    imshow("Image Dil", imgDil);
    imshow("Image", img);
    waitKey(0);
    return 0;
}

cvtColor(img, imgGray, COLOR_BGR2GRAY);
    GaussianBlur(img, imgBlur, Size(3, 3), 3, 0);
    Canny(imgBlur, imgCanny, 25, 75);
    Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
    dilate(imgCanny, imgDil, kernel);
    erode(imgDil, imgEro , kernel);//预处理

对图像进行预处理,转化为描边的二值图像

接下来重点,讲解核心自定义函数getContours(Mat imgDil, Mat img),看不懂的可以先跟着我下面的理解一步一步看

参数分别是描边二值图像和原图像

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

 findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

查找Dil图像中的形状,具体怎么用可以ai一下这里

vector<vector<Point>> contours如何理解这串代码

先看里面vector<Point>,表示点的数组,数组中的每一个元素代表一个点,一个数组包含数个点,可以理解为是一个轮廓

再看vector<vector<Point>>,以轮廓(以点为单位的数组)为单位,建立一个数组,数组里数个轮廓组成。

 vector<Vec4i> hierarchy;

存储轮廓关系的一个数组,可以理解是轮廓之间的父子关系(哪个轮廓在哪个里面)

for (int i = 0; i < contours.size(); i++)
    {

}

这个for循环表示阅历每一个轮廓

 int area = contourArea(contours[i]);
        cout << area << endl;

        if (area > 1000)

计算每个轮廓占的面积,并过滤掉噪音

vector<vector<Point>> conPoly(contours.size());
表示再建立一个只包含多边形的数组,目的是之后要把圆转为多边形,以便分辨图形(圆没有明显的识别特征,转化成多边形,特征便是多个端点)


 vector<Rect> boundRect(contours.size());

这个数组内是boundingRect输出的结果(矩形的数据(x, y, width, height))

float peri = arcLength(contours[i], true);

 approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);

先计算轮廓的周长peri,用 approxPolyDP把轮廓像多边形逼近,如把圆逼近至八边形。

int objCor = (int)conPoly[i].size();
            if (objCor == 3)
            {
                objectType = "Tri";
            }
            else if (objCor == 4)
            {
                float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
                if (aspRatio <= 1.05 && aspRatio >= 0.95)
                {
                    objectType = "Square";
                }
                else {
                    objectType = "Rect";
                }
                
            }
            else if (objCor > 4)
            {
                objectType = "Circle";
            }

识别各种形状

drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);

将轮廓逼近多边形后的轮廓在img图上用紫色画出来

rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(255,0,255), 2);

将轮廓中的点以一个最小面积的矩形围住

 putText(img, objectType, { boundRect[i].x, boundRect[i].y-5 }, FONT_HERSHEY_COMPLEX,
            0.5, Scalar(0,0,0), 1);

在对应位置写出轮廓的形状

下面展示代码效果

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐