入手opencv,最好的方法就是对应着程序进行学习是一个很好的方法,通过一些单个程序来了解图形处理的一些知识。这篇文章将介绍下面三个小任务。通道变换,灰度化,普通二值化。本文以C++版本进行代码介绍。
Q1:通道变换
一幅常规彩色的图像由BGR三通道组成,opencv提供cv::imread函数读取的彩色图像通道就是按照BGR的顺序进行排列的。opencv中也提供了模板类Vec,它可以表示一个向量。比如Vec3b可以表示一个8U的彩色图像,8U为8位无符号,除此之外Vec类定义了许多如下所示。
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;
typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;
typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;
例如:Vec3b color 定义一个8U的三通道彩色图像向量,那么有color[0],color[1],color[2],分别表示B,G,R三个通道的量,那么在程序表示时就要事先定义好一个零向量,用于通道变换赋值参量。代码行如下。三个参量分别为行,列和8位无符号三通道向量。
Mat out_img=Mat::zeros(imgrow,imgcol,CV_8UC3);
通道变换也就是BGR三个通道量进行互换,在代码中的体现与数据的交换类似,不过这个是在opencv下的通道向量互换。图像是由每个向量元素共同组成的矩阵向量,图像的三通道每个通道中都有相应的像素点,通道的变换需要遍历每个像素点进行变换赋值,这里使用opencv的at()函数,它可以实现读取矩阵中的某个像素,或者对某个像素进行赋值操作。整体代码如下。
#include"opencv2/highgui/highgui.hpp"
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
Mat Channel_swap(Mat img){
int imgcol=img.cols;
int imgrow=img.rows;
Mat out_img=Mat::zeros(imgrow,imgcol,CV_8UC3);
for(int j=0;j<imgrow;j++){
for(int i=0;i<imgcol;i++){ //BGR,8U的彩色图像表示为Vec3b
out_img.at<Vec3b>(j,i)[0] = img.at<Vec3b>(j,i)[2]; //R->B
out_img.at<Vec3b>(j,i)[2] = img.at<Vec3b>(j,i)[0]; //B->R
out_img.at<Vec3b>(j,i)[1] = img.at<Vec3b>(j,i)[1]; //G->G
}
}
return out_img;
}
int main()
{
Mat img=imread("ck567.jpg",IMREAD_COLOR);
Mat out_img=Channel_swap(img);
imshow("sample",out_img);
waitKey(0);
destroyAllWindows();
return 0;
}
通道变换由函数Channel_swap实现,返回类型是Mat类型,该函数的作用就是对每个通道内的像素值进行遍历重新赋值,达到变换通道的目的。
效果如下
Q2:灰度化
灰度化顾名思义就是将图片变为灰色,在opencv中可以用cvtColor函数将图像进行灰度化,但这里并不使用该函数,而是采用计算公式:Y = 0.2126*R + 0.7152* G + 0.0722* B,RGB分别为彩色图像的三通道。代码如下。
#include"iostream"
#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
Mat BGR2GRAY(Mat img){
int imgcol=img.cols;
int imgrow=img.rows;
Mat out=Mat::zeros(imgrow,imgcol,CV_8UC1);
for(int j=0;j<imgrow;j++){
for(int i=0;i<imgcol;i++){
out.at<uchar>(j,i)=0.2126*(float)img.at<Vec3b>(j,i)[2]+0.7152*(float)img.at<Vec3b>(j,i)[1]+0.0722*(float)img.at<Vec3b>(j,i)[0];
}
}
return out;
}
void main(){
Mat img=imread("ck567.jpg",IMREAD_COLOR);
Mat out=BGR2GRAY(img);
imshow("sample",out);
waitKey(0);
destroyAllWindows();
}
这里则zeros函数构建的是8位无符号单通道矩阵,原因是灰度图像就是单通道图像。效果如下
Q3:二值化
二值化是指将图片变为只有黑色和白色的图像,一幅图像的通道值的范围是0~255,0表示黑色,255表示白色,图片中的像素值介于两者之间,因此呈现出具有不同颜色的彩图,二值化就是将图片中的这些值修改为非0即255的状态。在二值化中需要一个阈值进行判断哪些值指定为0,哪些值指定为255。阈值的不同结果的图像也不同,这就需要在应用的时候进行阈值调整。图像的二值化需要先灰度化处理再进行二值化处理。图像二值化的作用很大,可以提取出图像的信息,在图像处理的应用中必不可少。
#include"iostream"
#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
Mat BGR2GRAY(Mat img){
int imgcol=img.cols;
int imgrow=img.rows;
Mat out=Mat::zeros(imgrow,imgcol,CV_8UC1);
for(int j=0;j<imgrow;j++){
for(int i=0;i<imgcol;i++){
out.at<uchar>(j,i)=0.2126*(float)img.at<Vec3b>(j,i)[2]+0.7152*(float)img.at<Vec3b>(j,i)[1]+0.0722*(float)img.at<Vec3b>(j,i)[0];
}
}
return out;
}
Mat Binaryzation(Mat grayimg,int threshold){
int grayimg_row = grayimg.rows;
int grayimg_col = grayimg.cols;
Mat out_img = Mat::zeros(grayimg_row,grayimg_col,CV_8UC1);
for(int j=0;j<grayimg_row;j++){
for(int i=0;i<grayimg_col;i++){
if(grayimg.at<uchar>(j,i)>threshold){
out_img.at<uchar>(j,i)=255;
}
else
out_img.at<uchar>(j,i)=0;
}
}
return out_img;
}
void main(){
Mat img = imread("ck567.jpg",IMREAD_COLOR);
Mat gray = BGR2GRAY(img);
Mat out = Binaryzation(gray,180);
imshow("sample",out);
waitKey(0);
destroyAllWindows();
}
效果如下,阈值分别为128,60和180。
,
这是针对图像的每个像素进行修改达到二值化,opencv提供了cvThreshold()函数可以直接二值化操作。