【基于C++和Python的Opencv3学习笔记之颜色空间缩减、ROI提取及多通道分离合并】

本文介绍了如何使用OpenCV进行颜色空间缩减,以减少图像处理的复杂性,并展示了如何选取图像的感兴趣区域(ROI)。此外,还详细讲解了如何分离与合并图像的RGB通道,以实现对图像特定颜色通道的操作。

颜色空间缩减

如果图像矩阵存储的是单通道像素,那么像素有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();
	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)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值