C++使用OpenCV实现证件照蓝底换成白底功能(或其他颜色如红色)详解

本文实例讲述了C++使用OpenCV实现证件照蓝底换成白底功能(或其他颜色如红色)。分享给大家供大家参考,具体如下:

今天刚好老师要办点事情,老师唯一的一张证件照是蓝色的,但是需要的底色是白色的,于是乎,好久不折腾的PS也忘记了,还好旁边的刚来的小学弟懂一点,

在那里慢慢的帮老师一点点的处理,PS在边缘的地方效果还真不咋地,确实是一门技术活。

于是我就想OpenCV能不能实现呢?一搜百度第一篇就是,但是人家转成红色,然后我又对HSV颜色空间不是很懂,最后在一个学习群里

但是文中未对HSV那一块做出解释,可能是我太菜了

贴出去问了下,一位优秀的本科生帮我清晰解答了,汗颜

主要步骤为:

1.把RGB图像转换到HSV空间

2.取背景的一小块20*20,计算蓝色背景的平均色调和饱和度

3.设置阈值,取出蓝色背景替换为红色背景

4.把HSV图像转换会RGB空间

5.滤波器去除边缘效应

具体代码为:

// change_color.cpp : 定义控制台应用程序的入口点。

//证件照从蓝色底换成红色底

//#include "stdafx.h"

#include <iostream>

#include <opencv2\core\core.hpp>

#include <opencv2\highgui\highgui.hpp>

#include <opencv2\imgproc\imgproc.hpp>

using namespace cv;

using namespace std;

int main()

{

char *origin="Original";

char *window="Image";

char *str="C:\\Users\\ltc\\Desktop\\nihao.jpg";

namedWindow(origin,1);

namedWindow(window,1);

Mat image=imread(str);

if(!image.data)

{

cout<<"图像载入出现问题"<<endl;

return 0;

}

Mat roi=image(Rect(20,20,20,20));

Mat hsvImg;

cvtColor(image, hsvImg, CV_BGR2HSV); //将图像转换到HSV颜色空间

//分离HSV空间,v[0]为H色调,v[1]为S饱和度,v[2]为v灰度

vector<Mat> v;

split(hsvImg,v);

Mat roiH=v[0](Rect(20,20,20,20));

Mat roiS=v[1](Rect(20,20,20,20));

int SumH=0;

int SumS=0;

int avgH, avgS;//蓝底的平均色调和平均饱和度

//取一块蓝色背景,计算出它的平均色调和平均饱和度

for(int i=0; i<20; i++)

{

for(int j=0; j<20; j++)

{

/*SumH=SumH+roiH(i,j);*/

SumH=int(roiH.at<uchar>(j,i))+SumH;

SumS=int(roiS.at<uchar>(j,i))+SumS;

}

}

avgH=SumH/400;

avgS=SumS/400;

//遍历整个图像

int nl=hsvImg.rows;

int nc=hsvImg.cols;

int step=10;

for(int j=0; j<nl; j++)

{

for(int i=0; i<nc; i++)

{

//以H.S两个通道做阈值分割,把蓝色替换成红色

if((v[0].at<uchar>(j,i))<=(avgH+5) && v[0].at<uchar>(j,i)>=(avgH-5)

&&(v[1].at<uchar>(j,i))<=(avgS+40) && v[1].at<uchar>(j,i)>=(avgS-40))

{

//cout<<int(v[0].at<uchar>(j,i))<<endl;

//红色底

//v[0].at<uchar>(j,i)=0;

//白色底

v[0].at<uchar>(j,i)=0;

v[1].at<uchar>(j,i)=0; //V[0]和V[1]全调成0就是变成白色

//绿色底

//v[0].at<uchar>(j,i)=60;

//蓝色底

//v[0].at<uchar>(j,i)=120;

/*cout<<int(v[0].at<uchar>(j,i))<<endl;*/

}

}

}

Mat finImg;

merge(v,finImg);

Mat rgbImg;

cvtColor(finImg,rgbImg, CV_HSV2BGR); //将图像转换回RGB空间

imshow(origin,image);

imshow(window,rgbImg);

//加个滤波把边缘部分的值滤掉(此处应该用低通滤波器,但感觉不太好,还是不用了。)

Mat result;

GaussianBlur(rgbImg,result,Size(3,3),0.5);

imshow(window,result);

imwrite("nihaoWhite.jpg",result);

waitKey(0);

//system("pause");

return 0;

}

////遍历整个图像

//int nl=hsvImg.rows;

//int nc=hsvImg.cols * hsvImg.channels();

//for(int j=0; j<nl; j++)

//{

// uchar *data=hsvImg.ptr<uchar>(j);

// for(int i=0; i<nc; i++)

// {

// cout<<int(data[i])<<" ";

// }

//}

这里面主要说明一下:

HSV模型

倒锥形模型:

这个模型就是按色彩、深浅、明暗来描述的。

H是色彩

S是深浅, S = 0时,只有灰度

V是明暗,表示色彩的明亮程度,但与光强无直接联系,(意思是有一点点联系吧)。

在这个程序里

色调主要是由V[0]来控制的

hsv是一个360度的模型 每个角度代表一种颜色

0度是红色

120度是绿色

240度是蓝色

但是OpenCV里最大值是255 所以它会对色调除以2,就是最大值是180

绿色对应的让它等于60    蓝色对应的就是120

换不同的背景只需要改动:

//红色底

v[0].at<uchar>(j,i)=0;

//白色底

v[0].at<uchar>(j,i)=0;

v[1].at<uchar>(j,i)=0; //V[0]和V[1]全调成0就是变成白色

//绿色底

v[0].at<uchar>(j,i)=60;

//蓝色底

v[0].at<uchar>(j,i)=120;

改动的位置就不需要说明了吧!这个方法的效果确实不错,大赞!

毕竟是老师的图片,不能轻易放出来,网上的也不能随便用吧!哈哈

那就放张我最爱的崩坏3吧!

附录

提取图像中指定颜色的像素区域

#include<iostream>

#include<opencv2/core/core.hpp>

#include<opencv2/imgproc/imgproc.hpp>

#include<opencv2/highgui/highgui.hpp>

using namespace cv;

class ColorDetector

{

private:

//最小可接受距离

int minDist;

//目标色

cv::Vec3b target;

//结果图像

cv::Mat result;

//计算与目标颜色的距离

int getDistance(cv::Vec3b color)

{

return abs(color[0] - target[0]) + abs(color[1] - target[1]) + abs(color[2] - target[2]);

}

public:

//空构造函数

ColorDetector() :minDist(100)

{

//初始化默认参数

target[0] = target[1] = target[2] = 0;

}

void setColorDistanceThreshold(int distance);

int getColorDistanceThreshold() const;

void setTargetColor(unsigned char red, unsigned char green, unsigned char blue);

void setTargetColor(cv::Vec3b color);

cv::Vec3b getTargetColor() const;

cv::Mat ColorDetector::process(const cv::Mat &image);

};

//设置色彩距离阈值,阈值必须是正的,否则设为0

void ColorDetector::setColorDistanceThreshold(int distance)

{

if (distance < 0)

distance = 0;

minDist = distance;

}

//获取色彩距离阈值

int ColorDetector::getColorDistanceThreshold() const

{

return minDist;

}

//设置需检测的颜色

void ColorDetector::setTargetColor(unsigned char red, unsigned char green, unsigned char blue)

{

//BGR顺序

target[2] = red;

target[1] = green;

target[0] = blue;

}

//设置需检测的颜色

void ColorDetector::setTargetColor(cv::Vec3b color)

{

target = color;

}

//获取需检测的颜色

cv::Vec3b ColorDetector::getTargetColor() const

{

return target;

}

cv::Mat ColorDetector::process(const cv::Mat &image)//核心的处理方法

{

//按需重新分配二值图像

//与输入图像的尺寸相同,但是只有一个通道

result.create(image.rows, image.cols, CV_8U);

//得到迭代器

cv::Mat_<cv::Vec3b>::const_iterator it = image.begin<cv::Vec3b>();

cv::Mat_<cv::Vec3b>::const_iterator itend = image.end<cv::Vec3b>();

cv::Mat_<uchar>::iterator itout = result.begin<uchar>();

for (; it != itend; ++it, ++itout)//处理每个像素

{

//计算离目标颜色的距离

if (getDistance(*it) < minDist)

{

*itout = 255;

}

else

{

*itout = 0;

}

}

return result;

}

int _tmain(int argc, _TCHAR* argv[])

{

//1.创建图像处理的对象

ColorDetector cdetect;

//2.读取输入图像

cv::Mat image = cv::imread("boldt.jpg");

if (!image.data)

{

return 0;

}

//3.设置输入参数

cdetect.setTargetColor(130, 190, 230);//蓝天的颜色

cv::namedWindow("result");

//4.处理并显示结果

cv::imshow("result", cdetect.process(image));

cv::waitKey();

return 0;

}

希望本文所述对大家C++程序设计有所帮助。

以上是 C++使用OpenCV实现证件照蓝底换成白底功能(或其他颜色如红色)详解 的全部内容, 来源链接: utcz.com/p/244674.html

回到顶部