文章目录

  • 笔记_2
    • 图像尺寸变换
      • resize 图像缩放 (重置图像大小)
      • flip 图像翻转
      • hconcat 横向连接
      • vconcat 纵向连接
    • 图像方式变换
      • warpAffine 仿射变换函数:矩阵M(2*3)
      • getRotationMatrix2D 获取图像旋转矩阵M:矩阵M(2*3)
      • getAffineTransform 获取仿射变换矩阵M:矩阵M(2*3)
    • 图像透视变换
      • getPerspectiveTransform :获取透视变换矩阵M(3*3)
      • warpPerspective :透视变换矩阵M(3*3)
    • 图像中绘制基本图形
      • line 绘制直线
      • arrowedLine 绘制带有箭头的直线
      • circle 绘制圆
      • ellipse 绘制椭圆形 ,其他参数如上
      • rectangle 绘制矩形 ,其他参数如上
      • fillPoly 绘制多边形 ,其他参数如上
      • putText 绘制文字 ,其他参数如上
    • ROI区域截取
      • Range
      • Rect_
      • copyTo 深拷贝数据
    • 高斯图像金字塔,拉普拉斯图像金字塔
      • pyrDown 下采样
      • pyrUp 上采样
    • 创建滑动条
      • createTrackbar
    • 鼠标事件响应
      • setMouseCallback 鼠标响应事件函数
      • MouseCallback 鼠标回调函数
    • 图像直方图的绘制
      • calcHist
    • 直方图均衡化
      • equalizeHist
      • normalize 数据归一化

笔记_2

图像尺寸变换

最邻近法选择临近的像素。简单,效果较差线性插值法选择临近两个像素,建立一次函数,进行投影,得到的数据双线性插值法选择临近的四个像素,四个像素两两建立一次函数,再把像素投影到建立好的一次函数上再次建立一次函数得到数据。应用图像的拉伸变换、旋转、仿射变换、透视变换等涉及到对图像像素位置改变的情况。

resize 图像缩放 (重置图像大小)

interpolation标志

flip 图像翻转

hconcat 横向连接

vconcat 纵向连接

Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");if (img.empty()){cout << "图像打开失败。。。" << endl;return -1;}//imwrite("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lenaleftTop.jpg", leftTop);//imwrite("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lenarightTop.jpg", rightTop);//imwrite("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lenaleftDown.jpg", leftDown);//imwrite("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lenarightDown.jpg", rightDown);Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);Mat smallImg, bigImg0, bigImg1, bigImg2;//先将图像缩小resize(gray, smallImg, Size(50, 50), 0, 0, INTER_AREA);//最近邻插值resize(smallImg, bigImg0, Size(100, 100), 0, 0, INTER_NEAREST);//双线性插值resize(smallImg, bigImg1, Size(100, 100), 0, 0, INTER_LINEAR);//双三次插值resize(smallImg, bigImg2, Size(100, 100), 0, 0, INTER_CUBIC);//翻转图像Mat img_x, img_y, img_xy;//等于0,沿x轴翻转flip(gray, img_x, 0);//大于0,沿y轴翻转flip(gray, img_x, 1);//小于0,沿xy轴翻转(原点翻转)flip(gray, img_x, -1);//平均分割图像四个部分//Range(*,*) 按照矩阵中的元素抠图Mat leftTop = Mat(img, Range(0, img.rows / 2), Range(0, img.cols / 2));Mat rightTop = Mat(img, Range(0, img.rows / 2), Range(img.cols / 2, img.cols));Mat leftDown = Mat(img, Range(img.rows / 2, img.rows), Range(0, img.cols / 2));Mat rightDown = Mat(img, Range(img.rows / 2, img.rows), Range(img.cols / 2, img.cols));//图像拼接Mat img0, img1, img_img;//图像横向连接,图像通道,高度y一致hconcat(leftTop, rightTop, img0);hconcat(leftDown, rightDown, img1);//图像纵向连接,图像通道,宽度x一致vconcat(img0, img1, img_img);

图像方式变换

warpAffine 仿射变换函数:矩阵M(2*3)

最常用的用特定值填充,BORDER_CONSTANT

getRotationMatrix2D 获取图像旋转矩阵M:矩阵M(2*3)

getAffineTransform 获取仿射变换矩阵M:矩阵M(2*3)

代码演示

Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");if (img.empty()){cout << "图像打开失败。。。" << endl;return -1;}Mat rotation0, img_warp0;//设置图像旋转的角度double angle = 30;//Size(宽,高) 设置输出图像的尺寸Size dst_size(img.rows+100, img.cols+100);//设置图像的旋转中心Point2f center(img.rows / 2.0, img.cols / 2.0);//计算仿射变换矩阵(旋转矩阵)rotation0 = getRotationMatrix2D(center, angle, 1);//进行仿射变换warpAffine(img, img_warp0, rotation0, dst_size);//根据定义的三个点进行仿射变换Point2f src_points[3];Point2f dst_points[3];src_points[0] = Point2f(0, 0);src_points[1] = Point2f(5, 50);src_points[2] = Point2f(30, 90);dst_points[0] = Point2f(2, 5);dst_points[1] = Point2f(33, 55);dst_points[2] = Point2f(100, 150);Mat rotation1, img_warp1;//计算仿射变换矩阵rotation1 = getAffineTransform(src_points, dst_points);//进行仿射变换warpAffine(img, img_warp1, rotation1, dst_size);

图像透视变换

getPerspectiveTransform :获取透视变换矩阵M(3*3)

warpPerspective :透视变换矩阵M(3*3)

代码演示

Mat img = imread("D:/OpenCV4.5.1/opencv/sources/modules/core/misc/objc/test/resources/chessboard.jpg");if (img.empty()){cout << "图像打开失败。。。" << endl;return -1;}Point2f src_points[4];Point2f dst_points[4];//获取原图像四个坐标src_points[0] = Point2f(224, 61);src_points[1] = Point2f(535, 46);src_points[2] = Point2f(231, 281);src_points[3] = Point2f(528, 302);//期望透视变换后四个坐标的位置dst_points[0] = Point2f(0, 0);dst_points[1] = Point2f(0, 500);dst_points[2] = Point2f(500, 0);dst_points[3] = Point2f(500, 500);Mat perspectiveMat, img_warp;//计算获取透视变换矩阵perspectiveMat = getPerspectiveTransform(src_points, dst_points);//进行透视变换warpPerspective(img, img_warp, perspectiveMat, Size(500,500));

图像中绘制基本图形

line 绘制直线

color:线条的颜色,可以是一个标量值,也可以是一个三元素元组,分别表示BGR三个通道的颜色值;thickness:线条的粗细,默认为1;lineType怎么理解" />arrowedLine 绘制带有箭头的直线 

arrowedLine函数是OpenCV库中用于在图像上绘制带有箭头的直线的函数。它可以在输入图像上绘制带有箭头的直线段。

以下是arrowedLine函数的C++函数签名:

void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, int line_type = LINE_8, int shift = 0, double tipLength = 0.1)

参数说明:

  • img:输入图像,可以是单通道或多通道图像。
  • pt1:直线的起点坐标。
  • pt2:直线的终点坐标。
  • color:绘制直线的颜色,以Scalar对象表示,可以是标量或RGB值。
  • thickness:直线的粗细,默认为1。
  • line_type:直线的类型,默认为LINE_8,表示8连接线。也可以选择LINE_4(4连接线)或LINE_AA(抗锯齿直线)。
  • shift:坐标点的小数位数,默认为0。
  • tipLength:箭头的长度与线段长度之比,默认为0.1,表示箭头长度为线段长度的10%。

circle 绘制圆

ellipse 绘制椭圆形 ,其他参数如上

rectangle 绘制矩形 ,其他参数如上

fillPoly 绘制多边形 ,其他参数如上

putText 绘制文字 ,其他参数如上

fontFace 标志如下图:

代码演示:

//创建三通道都为0Mat img = Mat::zeros(Size(512, 512), CV_8UC3);//绘制直线从开始点(100,100),结束点(200, 100),颜色(255, 255, 255)line(img, Point(100, 100), Point(200, 100), Scalar(255, 255, 255), 2, LINE_4, 0);//绘制一个实心圆,圆心(50, 50),半径25,颜色(255, 255, 255),-1表示填充circle(img, Point(50, 50), 25, Scalar(255, 255, 255), -1);//绘制一个空心圆,圆心(100, 50),半径20,颜色(255, 255, 255),轮廓宽度为4circle(img, Point(100, 50), 20, Scalar(255, 255, 255), 4);//绘制椭圆 ,圆心(300, 255),x半径100,y半径70,椭圆角度0,开始角度0,结束角度210(角度顺时针为正)ellipse(img, Point(300, 255), Size(100, 70), 0, 0, 210, Scalar(255, 255, 255), -1);//绘制矩形,左上角坐标(50, 400),右下角坐标(100, 450),颜色(255, 255, 255),-1表示填充rectangle(img, Point(50, 400), Point(100, 450), Scalar(255, 255, 255), -1);//绘制多边形Point pp[2][5];pp[0][0] = Point(72, 200);pp[0][1] = Point(142, 204);pp[0][2] = Point(226, 263);pp[0][3] = Point(172, 310);pp[0][4] = Point(177, 319);pp[1][0] = Point(447, 351);pp[1][1] = Point(504, 349);pp[1][2] = Point(484, 433);//pts变量的生成const Point *pts[2] = { pp[0],pp[1] };//顶点个数数组的生成const int npts[] = { 5,3 };//绘制2个多边形fillPoly(img, pts, npts, 2, Scalar(255, 255, 255), 8);//生成文字 putText(img, "learn OpenCv 4", Point(100, 130), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 255, 255));putText(img, "learn OpenCv 4", Point(100, 150), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 255, 255), 1, LINE_4, true);

ROI区域截取

Range

Rect_


copyTo 深拷贝数据

浅拷贝:Mat a;Mat b;//把b的头信息复制到a,数据都指向同一个数据a = b;/*浅拷贝是指拷贝了Mat对象的指针和数据头信息,但并没有拷贝数据本身。这种拷贝方式通常是通过赋值操作来实现的。浅拷贝后的对象和原始对象共享数据,因此当其中一个对象发生变化时,另一个对象也会受到影响。深拷贝是指拷贝了Mat对象的数据和数据头信息,生成了一个新的独立对象。这种拷贝方式通常是通过clone()函数来实现的。深拷贝后的对象和原始对象是独立的,互相不受影响。一般来说,当我们需要对Mat对象进行修改时,应该使用深拷贝,以免原始对象被意外修改。而在仅需要读取Mat对象时,使用浅拷贝可以节省内存开销和时间。clone()和copyTo()是OpenCV中两种不同的复制Mat对象的方法,它们的实现方式不同,但都可以用来创建一个独立的Mat对象,使其与原始对象互相独立,互不影响。clone()函数用于创建一个独立的Mat对象,它会复制原始对象的所有数据和元数据。所以,clone()函数会产生一个完全独立的对象,其内存空间与原始对象的内存空间是不同的。这意味着,在复制后,任何对新对象的更改都不会影响原始对象。*/cv::Mat img1 = cv::imread("image.jpg");// 读取原始图像cv::Mat img2 = img1.clone(); // 创建一个独立的Mat对象/*copyTo()函数也用于创建一个独立的Mat对象,但它可以将数据复制到一个现有的Mat对象中。如果目标Mat对象与原始Mat对象的大小和类型不同,则会自动进行缩放或类型转换。copyTo()函数还可以选择复制部分数据,而不是全部数据。*/cv::Mat img1 = cv::imread("image.jpg"); // 读取原始图像cv::Mat img2(img1.size(), img1.type()); // 创建一个新的Mat对象img1.copyTo(img2);// 将img1的数据复制到img2中/*因此,如果需要创建一个完全独立的Mat对象,可以使用clone()函数,而如果需要将数据复制到现有的Mat对象中,则可以使用copyTo()函数。无论使用哪种方法,都可以确保生成的新对象是独立的,不会影响原始对象。*/

代码演示:

Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");Mat orange = imread("D:/OpenCV4.5.1/opencv/sources/samples/data/orange.jpg");if (img.empty() || orange.empty()){cout << "图像打开失败。。。" << endl;return -1;}Mat ROI1, ROI2, ROI2_copy, mask, img2, img_copy;//重置图像orange像素到mask大小(200, 200)resize(orange, mask, Size(200, 200));//浅拷贝img2 = img; //深拷贝的两种方式img.copyTo(img_copy);//两种在图中截取ROI区域的方式:左上角坐标(150, 150),宽200,高200Rect rect(150, 150, 200, 200);//抠图ROI1 = img(rect);ROI2 = img(Range(300, 400), Range(300, 400));img(Range(300, 400), Range(300, 400)).copyTo(ROI2_copy);//ROI1 如果是已存在的矩阵大小和mask矩阵大小一致,直接把数据复制到ROI1原来的内存,//不一致的话,ROI1重新分配内存,不会把mask数据复制到ROI1原来的内存。mask.copyTo(ROI1);//img 和img2 和 ROI1 和 ROI2 数据都是在同一个内存中Mat clone1;//克隆数据img和clone1所有数据都一样,但是两个内存地址不一样clone1 = img.clone();

高斯图像金字塔,拉普拉斯图像金字塔


pyrDown 下采样

pyrUp 上采样

resize、pyrUp 和 pyrDown 图像金字塔(高斯金字塔、拉普拉斯金字塔)与尺寸缩放(向上采样、向下采样)resize 函数,最直接的方法。pyrUp 和 pyrDown 函数,即图像金字塔相关的两个函数,对图像进行向上采样和向下采样的操作。pyrUp 和 pyrDown 其实和专门用于放大缩小图像尺寸的 resize 在功能上差不多,批着图像金字塔的皮,说白了还是对图像进行放大和缩小操作。图像金字塔一幅图像的金字塔是一系列以金字塔形状排列,分辨率逐渐降低且源于同一张原始图的图像集合。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。层级越高,图像越小,分辨率越低。图像金字塔是图像中多尺度表达的一种,最初用于机器视觉和图像压缩,最主要功能用于图像分割,是一种以多分辨率来解释图像的有效但概念简单的结构。向上、向下采样图像金字塔中的向上和向下采样分别通过 pyrUp 和 pyrDown 实现。这里的向上向下采样,是针对图像的尺寸而言的(和金字塔的方向相反),向上就是图像尺寸加倍,向下就是图像尺寸减半。但需要注意的是,pyrUp 和 pyrDown 不是互逆的。对于 pyrUp,图像首先在每个维度上扩大为原来的两倍,新增的行和列(偶数行和列)以 0 填充。然后用指定的滤波器进行卷积(实际上是一个在每个维度上都扩大为原来两倍的过滤器)去估计”丢失“像素的近似值。对于 pyrDown,我们先要用高斯核对图像进行卷积,然后删除所有偶数行和偶数列,新的到的图像面积就会变成源图像的四分之一。高斯金字塔高斯金字塔是通过高斯平滑和亚采样获得的一系列采样图像。向下采样方法:① 对图像进行高斯内核卷积;② 将所有偶数行和列去除。向上采样方法:① 将图像在每个方向上扩大为原来的两倍,新增的行和列以 0 填充;② 使用原先同样的内核(乘以 4)与放大后的图像卷积,获得”新增像素“的近似值。在缩放过程中已经丢失了一些信息,如果想在缩放过程中减少信息的丢失,就需要用到拉普拉斯金字塔。
拉普拉斯金字塔拉普拉斯金字塔是通过源图像减去先缩小再放大的图像的一系列图像构成。

Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");if (img.empty()){cout << "图像打开失败。。。" << endl;return -1;}vector<Mat> Guass;int level = 3;//图从大到小保存到vectorGuass.push_back(img);for (int i = 0; i < level; i++){Mat guass;pyrDown(Guass[i], guass);Guass.push_back(guass);}vector<Mat> Lap;//图从小到到大保存到vectorfor (int i = Guass.size() - 1; i > 0; i--){Mat lap, upGuass;if (i == Guass.size() - 1){Mat down;pyrDown(Guass[i], down);pyrUp(down, upGuass);lap = Guass[i] - upGuass;Lap.push_back(lap);}pyrUp(Guass[i], upGuass);lap = Guass[i - 1] - upGuass;Lap.push_back(lap);}for (int i = 0; i < Guass.size(); i++){string name = to_string(i);imshow("G" + name, Guass[i]);imshow("L" + name, Lap[Guass.size()-1-i]);}

创建滑动条

createTrackbar

回调函数原型:typedef void (*TrackbarCallback)(int pos, void* userdata);

代码演示

//声明void callBack(int value, void *);int main(){Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");if (img.empty()){cout << "图像打开失败。。。" << endl;return -1;}namedWindow("img");imshow("img", img);int value = 100;createTrackbar("百分比", "img", &value, 600, callBack, &img);waitKey(0);return 0;}//回调函数void callBack(int value, void *data){Mat img = *(Mat*)data;float a = value / 100.0;imshow("img", img * a);}

鼠标事件响应

setMouseCallback 鼠标响应事件函数

MouseCallback 鼠标回调函数


代码演示:

//回调函数void mouseCB(int evnet,int x,int y,int flags,void *);//全局变量Point perPoint;int main(){Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");Mat imgPoint;if (img.empty()){cout << "图像打开失败。。。" << endl;return -1;}img.copyTo(imgPoint);string str1 = "img";imshow(str1, img);setMouseCallback(str1, mouseCB, &img);waitKey(0);return 0;}//回调函数void mouseCB(int event, int x, int y, int flags, void *data){Mat img = *(Mat*)data;//单击右键if (event == EVENT_RBUTTONDOWN){cout << "点击鼠标左键才可以绘制轨迹" << endl;}//单击左键,输出坐标if (event == EVENT_LBUTTONDOWN){//记录开始perPoint = Point(x, y);cout << "轨迹其实坐标: " <<perPoint<< endl;}//鼠标必须移动 并且 鼠标长按左键 flags & EVENT_FLAG_LBUTTON 在这里类似 flags == EVENT_FLAG_LBUTTONif (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)){Point pt(x, y);//划线line(img, perPoint, pt, Scalar(255, 255, 255), 3);perPoint = pt;imshow("img", img);}}

图像直方图的绘制

calcHist

hist保存的信息:

代码演示:

Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");if (img.empty()){cout << "图像打开失败。。。" << endl;return -1;}Mat gray;//bgr 2 graycvtColor(img, gray, COLOR_BGR2GRAY);//设置提取直方图的相关变量//用于存放直方图计算结果Mat hist;//通道索引const int channels[1] = { 0 };//直方图的维度,其实就是像素灰度值的最大值const int bins[1] = { 256 };float inRanges[2] = { 0,255 };//像素灰度值范围const float*ranges[1] = { inRanges };//计算图像直方图calcHist(&gray,1,channels,Mat(),hist,1,bins,ranges );//绘制直方图int hist_w = 512;int hist_h = 5000;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC1);for (int i = 0; i < hist.rows; i++){cout << "左上角: " << Point(width*(i), hist_h - 1) << endl;cout << "有下角: " << Point(width*(i + 1), hist_h - 1 - hist.at<float>(i)) << endl;rectangle(histImage, Point(width*(i), hist_h-1), Point(width*(i+1), hist_h - 1 - hist.at<float>(i)), Scalar(255, 255, 255), -1);//绘制直方图}namedWindow("img", WINDOW_NORMAL);imshow("img", histImage);{//设置提取直方图的相关变量//用于存放直方图计算结果Mat histB, histG, histR;//通道索引const int channels[1] = { 0 };//直方图的维度,其实就是像素灰度值的最大值const int bins[1] = { 256 };float inRanges[2] = { 0,255 };//像素灰度值范围const float*ranges[1] = { inRanges };Mat imgs[3];//分割三通道图像 按照BGR形式分割split(img, imgs);//计算图像直方图calcHist(&imgs[0], 1, channels, Mat(), histB, 1, bins, ranges);calcHist(&imgs[1], 1, channels, Mat(), histG, 1, bins, ranges);calcHist(&imgs[2], 1, channels, Mat(), histR, 1, bins, ranges);//绘制直方图int hist_w = 512;int hist_h = 5000;int width = 2;//创建三通道背景Mat histImageBGR = Mat::zeros(hist_h, hist_w, CV_8UC3);for (int i = 1; i < 256; i++){line(histImageBGR, Point(width*(i - 1), hist_h - cvRound(histB.at<float>(i - 1))), Point(width*(i), hist_h - cvRound(histB.at<float>(i))), Scalar(255, 0, 0), width);//绘制直方图line(histImageBGR, Point(width*(i - 1), hist_h - cvRound(histG.at<float>(i - 1))), Point(width*(i), hist_h - cvRound(histG.at<float>(i))), Scalar(0, 255, 0), width);//绘制直方图line(histImageBGR, Point(width*(i - 1), hist_h - cvRound(histR.at<float>(i - 1))), Point(width*(i), hist_h - cvRound(histR.at<float>(i))), Scalar(0, 0, 255), width);//绘制直方图}namedWindow("histImageBGR", WINDOW_NORMAL);imshow("histImageBGR", histImageBGR);}

直方图均衡化

equalizeHist

直方图均衡化的定义:将随机分布的直方图修改为均匀分布的直方图,其实质是对图像进行非线性拉伸,重新分配图像像元值,使一定灰度范围内的像元的数量大致相等。

normalize 数据归一化

归一化就是要把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。首先归一化是为了后面数据处理的方便,其次是保证程序运行时收敛加快。归一化的具体作用是归纳统一样本的统计分布性。归一化在0-1之间是统计的概率分布,归一化在某个区间上是统计的坐标分布。归一化有同一、统一和合一的意思。归一化的目的,是使得没有可比性的数据变得具有可比性,同时又保持相比较的两个数据之间的相对关系,如大小关系;或是为了作图,原来很难在一张图上作出来,归一化后就可以很方便的给出图上的相对位置等。

norm_type如下:

注意: 对于多通道数据,normalize()函数直接将其按内存中的顺序展开为数组,及当作一个向量进行处理。实例: vector positiveData = { 2.0, 8.0, 10.0 }; vector normalizedData_l1, normalizedData_l2, normalizedData_inf, normalizedData_minmax;范数归一化:1.1范数: // sum(numbers) = 20.0 //2.0+8.0+10.0// 2.0 0.1 (2.0/20.0) // 8.0 0.4 (8.0/20.0) // 10.0 0.5 (10.0/20.0) normalize(positiveData, normalizedData_l1, 1.0, 0.0同上最终归一化的值为单位向量的每个值乘以参数要归一化的范数值alpha。, NORM_L1);直接求和后算出每一个算数比上总和的比值,加起来总为1。这里要归一化的范数值为1.0,所求出的比值即为最后归一化后的值,若归一化范数值alpha为2.0,则每个比值分别乘以2.0即得到最后归一化后的结果为0.2, 0.8, 1.0,以此类推。2. 2范数: // Norm to unit vector: ||positiveData|| = 1.0//模长 = ((2.0)²+(8.0)²+(10.0)²)的结果开根号 = 12.961481396815720461931934872176 // 2.0 0.15 = 2 / 12.961481396815720461931934872176 // 8.0 0.62= 8 / 12.961481396815720461931934872176 // 10.0 0.77= 10 / 12.961481396815720461931934872176 normalize(positiveData, normalizedData_l2, 1.0, 0.0,NORM_L2);即将该向量归一化为单位向量,每个元素值除以该向量的模长。同上最终归一化的值为单位向量的每个值乘以参数要归一化的范数值alpha。3. 无穷范数 // Norm to max element // 2.0 0.2 (2.0/10.0) // 8.0 0.8 (8.0/10.0) // 10.0 1.0 (10.0/10.0) normalize(positiveData, normalizedData_inf, 1.0, 0.0,NORM_INF);每个值除以最大值来进行无穷范数归一化。同上最终归一化的值为单位向量的每个值乘以参数要归一化的范数值alpha。4:范围NORM_MINMAXnormalize函数中的norm_type参数表示归一化的方法,NORM_MINMAX表示使用最小值和最大值归一化。具体来说,对于输入的数组src,该方法会通过下面的公式对其进行归一化:dst(x,y) = (src(x,y) - minVal) * (beta - alpha) / (maxVal - minVal) + alpha其中,minVal和maxVal分别为src数组中的最小值和最大值,alpha和beta为指定的下限和上限。在归一化过程中,src数组中的所有元素都会被映射到指定的范围内。因此,使用NORM_MINMAX方法可以将图像的像素值归一化到指定的范围内,便于显示和后续处理。

代码演示:

//归一化并绘制直方图函数void drawHist(Mat &hist, int type, string name){//绘制直方图int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC1);//直方图hist数据归一化(这里归hist_h = 400化)normalize(hist, hist, hist_h, 0, type);for (int i = 0; i < hist.rows; i++){rectangle(histImage, Point(width*(i), hist_h - 1), Point(width*(i + 1), cvRound(hist_h - 1 - hist.at<float>(i))), Scalar(255, 255, 255), -1);//绘制直方图}imshow(name, histImage);}int main(){Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/Snipaste_2023-04-05_17-29-56shangu.png");if (img.empty()){cout << "图像打开失败。。。" << endl;return -1;}Mat gray, hist, hist2;//bgr 2 graycvtColor(img, gray, COLOR_BGR2GRAY);Mat equalImg;//将图像直方图均衡化必须是单通道equalizeHist(gray,equalImg);//通道索引const int channels[1] = { 0 };//直方图的维度,其实就是像素灰度值的最大值const int bins[1] = { 256 };float inRanges[2] = { 0,255 };//像素灰度值范围const float*ranges[1] = { inRanges };//计算图像直方图calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);calcHist(&equalImg, 1, channels, Mat(), hist2, 1, bins, ranges);drawHist(hist, NORM_INF, "hist");drawHist(hist2, NORM_INF, "hist2");namedWindow("原图", WINDOW_NORMAL);namedWindow("原图均衡化后", WINDOW_NORMAL);imshow("原图", gray);imshow("原图均衡化后", equalImg);waitKey(0);return 0;}