【基于C++和Python的Opencv3学习笔记之颜色空间缩减、ROI提取及多通道分离合并】
2018-01-28 16:00
781 查看
颜色空间缩减
如果图像矩阵存储的是单通道像素,那么像素有256种可能取值,但是如果是是三通道的图像,那么像素就有256×256×256种可能性,如此多的颜色会对我们处理产生较大的影响。实际上,仅用颜色中有代表性的很小部分就可以达到同样的效果了,这时候颜色空间缩减就显得尤为重要。颜色空间缩减的基本原理是:将现有颜色空间数除以某一特定值,以得到较少的颜色数,比如颜色值0~9取0, 10~19取1,以此类推。在Opencv中访问像素有三种方式,分别是基于指针访问(C操作符[])、基于迭代器iterator访问和动态地址计算。代码实现(基于C++)
#include "stdafx.h" #include<opencv2\highgui\highgui.hpp> #include<opencv2\core\core.hpp> #include<opencv2\imgproc\imgproc.hpp> using namespace std; using namespace cv; // 采用指针方式访问像素 void colorReduce_1(Mat &input, Mat &output, int div) { output = input.clone(); int rowNum = output.rows; int colNum = output.cols * output.channels(); for (int i = 0; i < rowNum; i++) { // ptr()函数获取图像任意行的首地址 uchar *data = output.ptr<uchar>(i); for (int j = 0; j < colNum; j++) { data[j] = data[j] / div * div / 2; } } } void colorReduce_2(Mat &input, Mat &output, int div) { output = input.clone(); // 迭代器首地址 Mat_<Vec3b>::iterator it = output.begin<Vec3b>(); // 迭代器的终止位置 Mat_<Vec3b>::iterator itend = output.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; } } void colorReduce_3(Mat &input, Mat &output, int div) { output = input.clone( b411 ); int rowNum = output.rows; int colNum = output.cols; for (int i = 0; i < rowNum; i++) { for (int j = 0; j < colNum; j++) { // 成员函数at()存储的图像元素,但是必须知道图像数据类型,调用形式如下: // image.at(i,j)[channel] = value; output.at<Vec3b>(i, j)[0] = output.at<Vec3b>(i, j)[0] / div * div + div / 2; output.at<Vec3b>(i, j)[1] = output.at<Vec3b>(i, j)[1] / div * div + div / 2; output.at<Vec3b>(i, j)[2] = output.at<Vec3b>(i, j)[2] / div * div + div / 2; } } } int _tmain(int argc, _TCHAR* argv[]) { namedWindow("原始图像"); namedWindow("指针访问"); namedWindow("迭代器访问"); namedWindow("动态地址访问"); // 图像初始化 Mat img = imread("1.jpg"); Mat dst1, dst2, dst3; dst1.create(img.size(), img.type()); dst2.create(img.size(), img.type()); dst3.create(img.size(), img.type()); // 第一种方式访问 colorReduce_1(img, dst1, 32); // 第二种方式访问 colorReduce_2(img, dst2, 32); // 第三种方式访问 colorReduce_3(img, dst3, 32); // 显示原图 imshow("原始图像", img); // 效果图1 imshow("指针访问", dst1); // 效果图2 imshow("迭代器访问", dst2); // 效果图3 imshow("动态地址访问", dst3); waitKey(0); return 0; }
代码实现(基于Python)
# coding=UTF-8 import numpy as np import cv2 def colorReduce_1(img, dst, div): ''' :param img: :param dst: :return: ''' m, n, c = np.shape(img) for i in range(m): for j in range(n): dst[i, j] = img[i, j] / div * div + div / 2 if __name__ =='__main__': cv2.namedWindow('原图') cv2.namedWindow('效果图') img = cv2.imread('1.jpg') dst = np.zeros(img.shape, np.uint8) colorReduce_1(img, dst, 32) cv2.imshow('原图', img) cv2.imshow('效果图', dst) cv2.waitKey(0)
ROI
在图像处理中,ROI是感兴趣区域,在图像中选择一块感兴趣的区域,以便我们进一步处理。使用ROI可以减少处理时间,增加精度,给图像处理带来便利。Opnecv定义ROI有两种方式,第一种方式如下:
Mat ROI;
ROI = image(Rect(x, y, cols, rows));
前两个参数是ROI左上角坐标,后两个参数是矩形的长宽。另一种方式是就是制定感兴趣的行或者列。
Mat ROI;
ROI = image(Range(y, y + rows), Range(x, x + cols));
下面我们通过一个实例来实现ROI:
代码实现(基于C++)
#include "stdafx.h" #include<opencv2\highgui\highgui.hpp> #include<opencv2\imgproc\imgproc.hpp> #include<opencv2\core\core.hpp> using namespace std; using namespace cv; int _tmain(int argc, _TCHAR* argv[]) { Mat image = imread("dota.jpg"); Mat logo = imread("logo.jpg"); // 划定感兴趣区域ROI Mat roi = image(Rect(200, 250, logo.cols, logo.rows)); // 叠加logo到ROI上 addWeighted(roi, 0.3, logo, 0.5, 0.0, roi); imshow("ROI叠加到原图上",image); waitKey(0); return 0; }
代码实现(基于Python)
# coding=UTF-8 import numpy as np import cv2 if __name__=='__main__': image = cv2.imread('dota.jpg') logo = cv2.imread('logo.jpg') height = logo.shape[0] width = logo.shape[1] roi = image[200: 200 + height, 250: 250 + width] cv2.addWeighted(roi, 0.3, logo, 0.5, 0.0, roi) cv2.imshow('', image) cv2.waitKey(0)
通道分离/合并
有时候为了更好的观察图像的特征,需要对图像的RGB的三个颜色通道的分量分别进行显示和处理,这时候可以用上通道分离函数split(),该函数的C++版本有两个原型:void split(const Mat &src, Mat *mvbegin);
void split(InputArray m, OutputArrayOfArray mv);
可以理解为第一个参数是输入的多通道图像,第二个参数是输出数组或者输出的vector容器。需要注意的事opencv中存储的顺序是BGR而不是RGB。
通道合并函数merge()函数是split()函数的逆向操作,将多个单通道图像合并成一个多通道图像,C++中函数原型为:
void merge(const Mat *mv., size_tcount, OutputArray dst);
void merge(InputArrayOfArray mv, OutputArray dst);
第一个参数是输入的矩阵或者vector容器的阵列,这里所有矩阵必须是一样的尺度和深度;第二个参数是当第一个参数是空白的C数组的时候,代表矩阵的个数,这个值必须大于1;第二个参数是输出矩阵。
下面用一个综合程序来实现分离和合并:
多通道分离和合并(基于C++的实现):
#include "stdafx.h" #include<opencv2\highgui\highgui.hpp> #include<opencv2\core\core.hpp> #include<opencv2\imgproc\imgproc.hpp> using namespace std; using namespace cv; int _tmain(int argc, _TCHAR* argv[]) { Mat logoImage; Mat image; Mat blueChannel, greenChannel, redChannel; vector<Mat>channel; // 蓝色部分 logoImage = imread("logo.jpg", 0); image = imread("dota.jpg"); split(image, channel); // Mat::at()返回一个引用到指定的数组元素(通道) blueChannel = channel.at(0); addWeighted(blueChannel(Rect(200, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0.0, blueChannel(Rect(200, 250, logoImage.cols, logoImage.rows))); merge(channel, image); imshow("游戏原画+logo蓝色通道", image); //绿色部分 logoImage = imread("logo.jpg", 0); image = imread("dota.jpg"); split(image, channel); greenChannel = channel.at(1); addWeighted(greenChannel(Rect(200, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0.0, greenChannel(Rect(200, 250, logoImage.cols, logoImage.rows))); merge(channel, image); imshow("游戏原画+logo绿色通道", image); // 红色部分 logoImage = imread("logo.jpg", 0); image = imread("dota.jpg"); split(image, channel); redChannel = channel.at(2); addWeighted(redChannel(Rect(200, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0.0, redChannel(Rect(200, 250, logoImage.cols, logoImage.rows))); merge(channel, image); imshow("游戏原画+logo红色通道", image); waitKey(0); return 0; }
多通道分离和合并(基于Python的实现):
# coding=UTF-8 import numpy as np import cv2 if __name__=='__main__': # 使用 Opencv 实现通道分离和合并 # 蓝色部分 image = cv2.imread('dota.jpg') logoImage = cv2.imread('logo.jpg', 0) height = logoImage.shape[0] width = logoImage.shape[1] blueImage, greenImage, redImage = cv2.split(image) cv2.addWeighted( blueImage[200: 200 + height, 250: 250 + width], 1, logoImage, 0.5, 0.0, blueImage[200: 200 + height, 250: 250 + width]) mergedBlue = cv2.merge([blueImage, greenImage, redImage]) cv2.imshow('游戏原画+logo蓝色通道', mergedBlue) # 红色部分 image = cv2.imread('dota.jpg') logoImage = cv2.imread('logo.jpg', 0) height = logoImage.shape[0] width = logoImage.shape[1] blueImage, greenImage, redImage = cv2.split(image) cv2.addWeighted(greenImage[200: 200 + height, 250: 250 + width], 1, logoImage, 0.5, 0.0, greenImage[200: 200 + height, 250: 250 + width]) mergedGreen = cv2.merge([blueImage, greenImage, redImage]) cv2.imshow('游戏原画+logo绿色通道', mergedGreen) # 红色部分 image = cv2.imread('dota.jpg') logoImage = cv2.imread('logo.jpg', 0) height = logoImage.shape[0] width = logoImage.shape[1] blueImage, greenImage, redImage = cv2.split(image) cv2.addWeighted(redImage[200: 200 + height, 250: 250 + width], 1, logoImage, 0.5, 0.0, redImage[200: 200 + height, 250: 250 + width]) mergedRed = cv2.merge([blueImage, greenImage, redImage]) cv2.imshow('游戏原画+logo绿色通道', mergedRed) # 使用 numpy 数组 # 蓝色部分 image = cv2.imread('dota.jpg') logoImage = cv2.imread('logo.jpg', 0) height = logoImage.shape[0] width = logoImage.shape[1] # 数组初始化 b = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) g = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) r = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) # 复制数据 b[:, :] = image[:, :, 0] g[:, :] = image[:, :, 1] r[:, :] = image[:, :, 2] # 叠加 cv2.addWeighted(b[250: 250 + height, 200: 200 + width], 1.0, logoImage, 0.5, 0.0, b[250: 250 + height, 200: 200 + width]) mergedBlueNp = np.dstack([b, g, r]) cv2.imshow('', mergedBlueNp) # 绿色部分 image = cv2.imread('dota.jpg') logoImage = cv2.imread('logo.jpg', 0) height = logoImage.shape[0] width = logoImage.shape[1] # 数组初始化 b = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) g = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) r = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) # 复制数据 b[:, :] = image[:, :, 0] g[:, :] = image[:, :, 1] r[:, :] = image[:, :, 2] # 叠加 cv2.addWeighted(g[250: 250 + height, 200: 200 + width], 1.0, logoImage, 0.5, 0.0, g[250: 250 + height, 200: 200 + width]) mergedGreenNp = np.dstack([b, g, r]) cv2.imshow('', mergedGreenNp) # 红色部分 image = cv2.imread('dota.jpg') logoImage = cv2.imread('logo.jpg', 0) height = logoImage.shape[0] width = logoImage.shape[1] # 数组初始化 b = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) g = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) r = np.zeros((image.shape[0], image.shape[1]), dtype=image.dtype) # 复制数据 b[:, :] = image[:, :, 0] g[:, :] = image[:, :, 1] r[:, :] = image[:, :, 2] # 叠加 cv2.addWeighted(r[250: 250 + height, 200: 200 + width], 1.0, logoImage, 0.5, 0.0, r[250: 250 + height, 200: 200 + width]) mergedRedNp = np.dstack([b, g, r]) cv2.imshow('', mergedRedNp) cv2.waitKey(0)
相关文章推荐
- python3-opencv库(3)--图片颜色空间转换,利用HSV进行物体跟踪,图像通道分离与合并
- OpenCV之Python学习笔记(1)(2): 图像的载入、显示和保存 图像元素的访问、通道分离与合并
- 【OpenCV学习笔记】【函数学习】九(RGB空间各通道分离)--Mat类型
- opencv学习笔记(一):基于YCrCb颜色空间的肤色检测
- Python笔记——python OpenCV 图像通道分离和合并
- 【基于C++和Python的Opencv3学习笔记之图像载入、显示和保存】
- 【基于C++和Python的Opencv3学习笔记之滑动条的使用】
- openCV—Python(7)—— 彩色图像R、G、B分量的提取与合并及其相关颜色空间的转化
- 【OpenCV学习笔记】【函数学习】九(RGB空间各通道分离)
- openCV学习笔记-颜色通道的分离和混合
- 【基于C++和Python的Opencv3学习笔记之基本图形的绘制】
- 【OpenCV学习笔记】十二、图像的对比度和亮度调整及图像通道的分离与合并
- python OpenCV学习笔记(八):改变颜色空间
- 像素临近区域、颜色通道、ROI区域的访问---opencv.2学习笔记2
- OpenCv学习笔记(四):使用split函数提取彩色图像的三个通道
- Kinect开发学习笔记之(四)提取颜色数据并用OpenCV显示
- Opencv学习笔记(2)模块,图像读取、显示、叠加、融合、颜色分离、亮度、对比度
- opencv学习(5)图像像素的访问、颜色通道的分离和融合
- Kinect开发学习笔记之(四)提取颜色数据并用OpenCV显示
- OpenCV学习笔记_图像转换cvCvtColor、通道分割cvSplit以及通道合并cvMerge