一.cvtColor的用处和API讲解
1.cvtColor的作用
cvtColor是OPENCV里面颜色转换的转换函数,它的功能非常强大。能够实现RGB图像转换成灰度图、灰度图转换成RGB图像、RGB转换成HSV等等。下面我们来看看
2.cvtColor的API
CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );
第一个参数:输入的图像数据
第二个参数:输出的图像数据
第三个参数:颜色转换的标识符,下面是转换的图表。图像转化可以分为11个大类,分别是RGB->BGR、RGB->5X5、 RGB->GRAY、RGB->CIEXYZ、RGB->YyCrcb(YUV)、RGB->HSV、RGB->HLS、RGB->CIELab、RGB->CIELuv、RGB->Bayer、YUV420->RGB。
常用的:
COLOR_BGR2GRAY
:彩色图像转灰度图COLOR_GRAY2BGR
:灰度图转彩色图像(每个通道值相同)
COLOR_BGR2HSV
:BGR 转 HSV(色相、饱和度、亮度)COLOR_HSV2BGR
:HSV 转 BGR
COLOR_BGR2YCrCb
:BGR 转 YCrCb(亮度、红色差、蓝色差)COLOR_YCrCb2BGR
:YCrCb 转 BGR
Y:亮度分量
Cr:红色分量与亮度的差值
Cb:蓝色分量与亮度的差值
COLOR_BGR2Lab
:BGR 转 Lab(亮度、a 通道、b 通道)COLOR_Lab2BGR
:Lab 转 BGR
L:亮度,0-100
a:从绿色到红色的范围
b:从蓝色到黄色的范围
COLOR_BGR2YUV
:BGR 转 YUV(亮度、色度)COLOR_YUV2BGR
:YUV 转 BGR
第四个参数:目标图像通道数,默认为0
二.用代码实现cvtColor的颜色转换功能
1.流程
这次代码主要是转换几个常见的格式,如RGB->YUV, YUV->RGB。具体的代码思路如下:
这个代码里面我们分别读取两种图片,一种是RGB图片、另外一种是YUV灰度图像、分别用cvtColor把RGB图像转换成gray图像、把灰度图像转换成RGB图像与YUV图像、最后用imwrite保存两种图片。
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>using namespace cv;
using namespace std;int main()
{// 读取彩色图片Mat img1 = imread("people.jpg");if(img1.empty()){cerr << "无法读取彩色图片 people.jpg" << endl;return -1;}cout << "成功读取彩色图片,通道数: " << img1.channels() << endl;// 读取灰度图片Mat img2 = imread("gray.jpeg", IMREAD_GRAYSCALE);if(img2.empty()){cerr << "无法读取灰度图片 gray.jpeg" << endl;return -1;}cout << "成功读取灰度图片,通道数: " << img2.channels() << endl;// 正确转换:BGR转灰度Mat img1_gray;cvtColor(img1, img1_gray, COLOR_BGR2GRAY);if(img1_gray.empty()){cerr << "彩色图转灰度图失败" << endl;return -1;}cout << "彩色图转灰度图成功,通道数: " << img1_gray.channels() << endl;// 正确转换:灰度图转回BGR彩色图Mat img2_bgr;cvtColor(img2, img2_bgr, COLOR_GRAY2BGR);if(img2_bgr.empty()){cerr << "灰度图转彩色图失败" << endl;return -1;}cout << "灰度图转彩色图成功,通道数: " << img2_bgr.channels() << endl;// 如果确实需要YUV格式,可以这样转换Mat img2_yuv;cvtColor(img2_bgr, img2_yuv, COLOR_BGR2YUV);if(img2_yuv.empty()){cerr << "BGR转YUV失败" << endl;return -1;}cout << "BGR转YUV成功,通道数: " << img2_yuv.channels() << endl;// 保存处理后的图像imwrite("img1_gray.jpg", img1_gray);imwrite("img2_bgr.jpg", img2_bgr);imwrite("img2_yuv.jpg", img2_yuv);cout << "所有图像保存成功" << endl;return 0;
}
原图 处理后
原图 yuv bgr
2.错误分析
1.OpenCV 默认读取的是 BGR 格式,应该使用COLOR_BGR2GRAY,而不是COLOR_RGBGRAY,虽然不会有错误,也可以使用,但是不规范。
2.灰度图转换出现的问题
读取灰度图的时候不可以直接 Mat img2 = imread("gray.jpeg");
不指定参数时,imread
默认以彩色模式(IMREAD_COLOR
)读取图像,即使原图是灰度图。
即使原图是灰度图,OpenCV 仍会将其读取为 3 通道(BGR 格式)图像,每个通道的值相同。
例如,原图中像素值为 128
的点,会被读取为 [128, 128, 128]
(BGR 三个通道)。
所以需要这样子读取
// 读取灰度图片
Mat img2 = imread("gray.jpeg", IMREAD_GRAYSCALE);
指定 IMREAD_GRAYSCALE
的效果
强制 OpenCV 将图像读取为单通道(灰度)图像,无论原图是什么格式。
图像数据将被压缩为单通道,减少内存占用,且符合后续灰度图像处理的预期。
为什么要这样子?
如果直接使用 imread("gray.jpeg")
(不指定参数),可能引发以下问:
1.通道数不匹配:
若后续代码假设 img2
是单通道灰度图(如调用 COLOR_GRAY2BGR
),但实际读取的是 3 通道彩色图,会触发 "Invalid number of channels" 错误。如下
terminate called after throwing an instance of 'cv::Exception'what(): OpenCV(3.4.12) /home/yifeng/Downloads/opencv-3.4.12/modules/imgproc/src/color.simd_helpers.hpp:88: error: (-2:Unspecified error) in function 'cv::impl::{anonymous}::CvtHelper<VScn, VDcn, VDepth, sizePolicy>::CvtHelper(cv::InputArray, cv::OutputArray, int) [with VScn = cv::impl::{anonymous}::Set<1>; VDcn = cv::impl::{anonymous}::Set<3, 4>; VDepth = cv::impl::{anonymous}::Set<0, 2, 5>; cv::impl::{anonymous}::SizePolicy sizePolicy = (cv::impl::<unnamed>::SizePolicy)2; cv::InputArray = const cv::_InputArray&; cv::OutputArray = const cv::_OutputArray&]'
> Invalid number of channels in input image:
> 'VScn::contains(scn)'
> where
> 'scn' is 3Aborted (core dumped)
2.占用内存
灰度图本身只需 1 通道存储,但被读取为 3 通道会占用 3 倍内存
三.putText的用处和API讲解
1. putText的用法和作用
putText是OPENCV中常见的功能,他主要的功能是在Mat矩阵里面显示显示文字,像下图
从上图可以看出来putText是通过坐标(X,Y)在矩阵显示文字
2. putText的API
CV_EXPORTS_W void putText( InputOutputArray img, const String& text,Point org,int fontFace, double fontScale, Scalar color,int thickness = 1, int lineType = LINE_8,bool bottomLeftOrigin = false );
第一个参数:img需要传入的图像数据
第二个参数:text需要显示的文字
第三个参数:org文字在图像数据中的坐标位置
第四个参数:fontFace字体类型,常用的字体类型如下:
基本无衬线字体(Sans-Serif)
FONT_HERSHEY_SIMPLEX,
最简单的无衬线字体,笔画粗细一致,字符结构清晰。常用于需要简洁、易读的文本标注,如物体检测结果、图像分析数据等。
FONT_HERSHEY_PLAIN,比 SIMPLEX 更简单的变体,字符更细、更紧凑。适合空间有限的场景,如小尺寸图像中的标注。
FONT_HERSHEY_DUPLEX,比 SIMPLEX 更复杂的无衬线字体,部分字符有双线条效果。视觉上更丰富,但保持了良好的可读性。
复杂无衬线字体
FONT_HERSHEY_COMPLEX,复杂的无衬线字体,字符结构更精细,部分笔画有变化。适合需要一定美观度的文本,如演示文稿中的图像标注。
FONT_HERSHEY_TRIPLEX,最复杂的无衬线字体,类似 DUPLEX 但更精细,某些字符有三线条效果。视觉效果丰富,常用于需要强调的文本。
FONT_HERSHEY_COMPLEX_SMALL,COMPLEX 的小型变体,字符更小但保持了相同的结构复杂度。适合需要在小空间内显示详细信息的场景。
手写风格字体
FONT_HERSHEY_SCRIPT_SIMPLEX,手写风格的简化字体,模拟手写体的流畅性。适合需要个性化或非正式风格的文本。
FONT_HERSHEY_SCRIPT_COMPLEX,更复杂的手写风格字体,笔画变化更丰富,接近书法效果。常用于需要艺术感的文本,如标题或装饰性文字。
以上所有类型都可以配合 FONT_HERSHEY_ITALIC使用,产生斜体效果
第五个参数:fontScale字体的大小
第六个参数:color是颜色标量,字体的显示颜色
第七个参数:thickness是字体的粗细程度,默认为1
第八个参数:lineType线性,默认是LINE_8,具体的几个如下:
第九个参数:bottomLeftOrigin图像数据原点在左下角, Otherwise(默认false)图像数据中原点的左上角。默认bottomLeftOrigin = false
四.putText代码实战
1.cvColor
2.putText
流程:
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>using namespace cv;
using namespace std;int main()
{Mat img = imread("people.jpg");if(img.empty()){cerr<<"imread people.jpg failed\n "<<endl;std::cout<<"imread people.jpg failed\n "<<std::endl;}cerr<<"imread people.jpg successed\n "<<endl;std::cout<<"imread people.jpg successed\n "<<std::endl;string str="hello world 你好世界!";//要现实的文本//参数三:显示的位置Point located;located.x = 0;located.y=400;//参数四:字体格式int fontFace = cv::FONT_HERSHEY_SCRIPT_SIMPLEX;//字体大小double fontScale = 5.0;//字体粗细int thickness = 4;putText(img,str,located,fontFace,fontScale,Scalar(255,0,0),thickness,LINE_8);imwrite("putText.jpg",img);
return 0;
}
我们代码中显示内容我们输入了中文,可结果却无法显示,这是因为putText它不提供字库,显示中文需要使用freeType等文字库。