您的位置:首页 > 编程语言 > C语言/C++

【基于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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐