OpenCV学习笔记(三)

    科技2024-03-22  95

    关于Point类特殊要说的:

    typedef Point2i Point1; typedef Point_<int> Pointi; typedef Point2f Point2; typedef Point_<float> Pointf;

    这些类型根据其基于的基本数据类型,之间都有等价关系。

    Scalar:其本质就是4个元素的数组,在opencv中大量被用于存储像素值,例如RGB元素值。

    切记:用几个参数,写几个,别没事找事!

    Size:size(int width,int height)构建的时候给定宽高,再没别的用法

    Rect:x,y左上点坐标,width,height矩形宽高,area返回面积,contain(Point)判断点是否在矩形里,inSide(Rect)判断矩阵是否在矩阵里,tl()返回左上点左边,br()返回右下角坐标

    色彩空间转换:void cvtColor(InputArray src,OutputArray dst,int code,int dstCn = 0);

    第三个参数:色彩空间转换的表示符,理解着用,例如BGR转HSV COLOR_BGR2HSV,这个2就是to。

    第四个参数:默认值0,表示目标图像获取原图像的通道数。基本上用不到,不输参数,默认值就好了。

    基本图像绘制:line直线,ellipse椭圆,rectangle矩形绘制函数,circle圆,fillPoly函数(填充多边形)

    具体参数看vs的提示和下面示例测试程序

    #include <opencv2/core/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #define WINDOW_NAME1 "【绘制图1】" //为窗口标题定义的宏 #define WINDOW_NAME2 "【绘制图2】" //为窗口标题定义的宏 #define WINDOW_WIDTH 600//定义窗口大小的宏 using namespace cv; void DrawEllipse(Mat img, double angle);//绘制椭圆 void DrawFilledCircle(Mat img, Point center);//绘制圆 void DrawPolygon(Mat img);//绘制多边形 void DrawLine(Mat img, Point start, Point end);//绘制线段 int main(int argc, char** argv) { Mat src = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3); double angle = 0.0; DrawEllipse(src, angle); DrawLine(src, Point(20, 20), Point(100, 100)); DrawFilledCircle(src, Point(150, 150)); DrawPolygon(src); imshow("画布", src); waitKey(0); } void DrawEllipse(Mat img, double angle) { int thickness = 2; int lineType = 8; ellipse(img,//画布 Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2),//圆心 Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16),//椭圆尺寸 angle,//椭圆旋转角度 0,//拓展的弧度0-360 360, Scalar(255, 129, 0),//通道颜色,蓝色 thickness,//线宽 lineType);//线型 } void DrawLine(Mat img, Point start, Point end) { int thickness = 2; int lineType = 8; line(img,//输出图像 start,//初始点 end,//结束点 Scalar(255, 129, 0),//通道颜色。蓝色 thickness,//线宽 lineType);//线型 } void DrawFilledCircle(Mat img, Point center) { //线宽为-1,则会用通道颜色填充圆 //正整值,则是正常的空心圆 int thickness = -1; int lineType = 8; circle(img, center,//圆心 WINDOW_WIDTH / 32,//半径 Scalar(255, 129, 0),//通道颜色,蓝色 thickness,//线宽 lineType);//线型 } void DrawPolygon(Mat img) { int lineType = 8; //创建一些点 Point rookPoints[1][20]; rookPoints[0][0] = Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8); rookPoints[0][1] = Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8); rookPoints[0][2] = Point(3 * WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16); rookPoints[0][3] = Point(11 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16); rookPoints[0][4] = Point(19 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8); rookPoints[0][5] = Point(3 * WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8); rookPoints[0][6] = Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8); rookPoints[0][7] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8); rookPoints[0][8] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4); rookPoints[0][9] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4); rookPoints[0][10] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8); rookPoints[0][11] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8); rookPoints[0][12] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4); rookPoints[0][13] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4); rookPoints[0][14] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8); rookPoints[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8); rookPoints[0][16] = Point(WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8); rookPoints[0][17] = Point(13 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8); rookPoints[0][18] = Point(5 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16); rookPoints[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16); const Point* ppt[1] = { rookPoints[0] }; int npt[] = { 20 }; fillPoly(img, ppt, npt, 1, Scalar(255, 255, 255), lineType); }

    图像在内存中的存储方式:

    灰度图像:

    多通道图像:

    Opencv中的子列存储顺序是反过来的,BGR而不是RGB(了解)

    色彩空间缩减:

    由于多通道图像,像素值太太太多,决定将现有色彩空间除以某个输入值,以获得较少的颜色数。

    0~9取0,10~19取10,类似这样。

    LUT函数:批量进行图像元素查找,扫描与操作图像。

    函数原型:void LUT(InputArray src, InputArray lut, OutputArray dst);

    一参:输入图像(单,多通道都可)

    二参:查找表(与输入图像通道数保持一致)

    三参:输出图像

    具体使用方法:

    #include <opencv2/highgui/highgui.hpp> using namespace cv; int main(int argc, char** argv) { uchar lutData[256]; for (int i = 0; i < 256; i++) { if (i <= 100) { lutData[i] = 0; } if (i > 100 && i <= 200) { lutData[i] = 100; } if (i > 200) { lutData[i] = 255; } } Mat lut(1, 256, CV_8UC1, lutData);//1行,256列,像素类型为8字节无符号char类型1通道,通道已定义的uchar类型数组 Mat src = imread("E://hashiqi.jpg", IMREAD_GRAYSCALE); Mat dst; LUT(src, lut, dst); imshow("///", src); imshow("------", dst); waitKey(0); }

    其中测试程序的代码不难理解,这个lut查找表的机制原理,暂时没搞懂,之后再说

    计时函数(常用):getTickCount():返回某个事件以来走过的时钟周期数

                                getTickFrequency():返回cpu一秒钟所走过的时钟周期数

    这样用

    访问图片像素的三种方法:

    1.指针访问:

    mat.ptr<type>(row)[col],没啥好说的,用就完事了

    void colorReduce(Mat& inputImage, Mat& outputImage, int div) { //参数准备 outputImage = inputImage.clone(); //拷贝实参到临时变量 int rowNumber = outputImage.rows; //行数 int colNumber = outputImage.cols * outputImage.channels(); //列数 x 通道数=每一行元素的个数 //双重循环,遍历所有的像素值 for (int i = 0; i < rowNumber; i++) //行循环 { uchar* data = outputImage.ptr<uchar>(i); //获取第i行的首地址 for (int j = 0; j < colNumber; j++) //列循环 { // ---------【开始处理每个像素】------------- /*data[j] = data[j] / div * div + div / 2;*/ std::cout << (int)data[j]; // ----------【处理结束】--------------------- } //行处理结束 } }

    2.迭代器iterator:

    获取图像矩阵的begin和end,然后解指针访问像素内容,遍历跟寻常迭代器一致

    void colorReduce(Mat& inputImage, Mat& outputImage, int div) { //参数准备 outputImage = inputImage.clone(); //拷贝实参到临时变量 //获取迭代器 Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //初始位置的迭代器 Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //终止位置的迭代器 //存取彩色图像像素 for (; it != itend; ++it) { // ------------------------【开始处理每个像素】-------------------- //(*it)[0] = (*it)[0] / div * div + div / 2; //(*it)[1] = (*it)[1] / div * div + div / 2; //(*it)[2] = (*it)[2] / div * div + div / 2; std::cout << (*it)[0]; //多通道的话就再自行发挥语句 // ------------------------【处理结束】---------------------------- } }

    3.动态地址计算:

    没啥说的,at<像素类型>[通道下标],就可

    void colorReduce(Mat& inputImage, Mat& outputImage, int div) { //参数准备 outputImage = inputImage.clone(); //拷贝实参到临时变量 int rowNumber = outputImage.rows; //行数 int colNumber = outputImage.cols; //列数 //存取彩色图像像素 for (int i = 0; i < rowNumber; i++) { for (int j = 0; j < colNumber; j++) { // ------------------------【开始处理每个像素】-------------------- std::cout << outputImage.at<Vec3b>(i, j)[0]; std::cout << outputImage.at<Vec3b>(i, j)[1]; std::cout << outputImage.at<Vec3b>(i, j)[2]; //outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div * div + div / 2; //蓝色通道 //outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div * div + div / 2; //绿色通道 //outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div * div + div / 2; //红是通道 // -------------------------【处理结束】---------------------------- } // 行处理结束 } }

    分离颜色通道函数split,多通道图像混合merge

    void split(const Mat &src,Mat *mvbegin)

    void split(InputArray m,OutputArrayOfArrays mv);

    void merge(const Mat* mv,size_tcount,OutputArray dst)

    void merge(InputArrayofArrays mv,OutputArray dst)

    split 和 merge 是互逆操作,用法,参数均类似

    at方法是取的引用,修改值,原值也会变动

    #include <opencv2/core/core.hpp> #include <opencv2/opencv.hpp> #include <opencv2/imgproc.hpp> using namespace std; using namespace cv; int main(int argc, char** argv) { Mat src = imread("E://hashiqi.jpg"); Mat imageROI,image2_ROI,image3_ROI; Mat dst; vector<Mat> channels; split(src, channels); imageROI = channels.at(0);//多通道图像分离通道号,像素值从0~255,理论上来说应该是灰度图像 image2_ROI = channels.at(1); image3_ROI = channels.at(2); merge(channels, dst);//合并三通道 imshow("merge", dst); imshow("split", imageROI); waitKey(0); }

    点操作(仅通过输入的像素值变化,最多加上些全局信息):亮度调整,对比度调整,颜色校正,变换

    f(x):原图像素,g(x):输出图像素,a>0称增益,控制图像对比度,b称偏置,控制图像亮度

    下面是使用方法的测试程序:

    #include <opencv2/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace std; using namespace cv; int ContrastValue; //对比度值 int BrightValue; //亮度值 Mat src, dst;//输入图像,输出图像 static void ContrastAndBright(int, void*); int main(int argc, char** argv) { system("color 2F"); src = imread("E://hashiqi.jpg"); dst = Mat::zeros(src.size(), src.type());//初始化dst尺寸 namedWindow("【原始图窗口】",1); ContrastValue = 80; BrightValue = 80; createTrackbar("对比度", "【原始图窗口】", &ContrastValue, 300,ContrastAndBright);//ContrastAndBright实现修改功能的回调函数 createTrackbar("亮度", "【原始图窗口】", &BrightValue, 200, ContrastAndBright); ContrastAndBright(ContrastValue, 0); ContrastAndBright(BrightValue, 0); waitKey(0); } static void ContrastAndBright(int, void*) { namedWindow("【原始图窗口】", 1); for (int y = 0; y < src.rows; y++) { for (int x = 0; x < src.cols; x++) { for (int c = 0; c < 3; c++) { dst.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((ContrastValue * 0.01) * (src.at<Vec3b>(y, x)[c]) + BrightValue); } } } // 显示图像 imshow("【原始图窗口】", src); imshow("【效果图窗口】", dst); }

    Processed: 0.013, SQL: 8