C++?opencv將圖片動漫化介紹
最近對圖像處理十分感興趣,也學(xué)著用opencv 實現(xiàn)各種簡單的圖像處理,因此,有了下面的實驗,就是將照片處理成漫畫的風(fēng)格。
對照片進行動漫話一般需要四個步驟
1、邊緣檢測
2、將邊緣檢測得到的邊緣 以黑色的形式貼在原來的畫上。
3、對貼了邊緣的圖進行雙邊濾波,雙邊濾波可以較好的濾波的同時保留邊緣。
4、修改圖像的顏色的飽和度,本文采用的是將RGB轉(zhuǎn)化為HSI空間,然后調(diào)整S分量。
邊緣檢測
對于邊緣檢測,本文采用的是canny算法
此文中將低閾值設(shè)定在70,高閾值則為70*3。
執(zhí)行后的結(jié)果為:

貼邊緣圖到原圖
將邊緣圖以黑色貼到原圖上,原圖上非邊緣區(qū)域仍然為原來的顏色,動漫就是邊緣很明顯,且邊緣不是很多,不注重細節(jié),因此這里將邊緣貼上面當(dāng)作邊緣,后續(xù)利用雙倍濾波將圖中的其他相對小的細節(jié)邊緣去掉。針對紋理貼圖主要用到下面這個函數(shù):
// 將邊緣檢測后的圖 cannyImage 邊以黑色的形式貼在原圖 image上。
void pasteEdge(Mat &image, Mat &outImg, IplImage cannyImage)
{
Mat cannyMat;
//將IplImage轉(zhuǎn)化為Mat
cannyMat = cvarrToMat(&cannyImage);
//顏色反轉(zhuǎn)
cannyMat = cannyMat < 100;
image.copyTo(outImg, cannyMat);
}執(zhí)行后的效果如下:

雙邊濾波
雙邊濾波(Bilateral filter)在圖像美化,美顏上有廣泛的運用,是一種可以保邊去噪的濾波器,由兩個函數(shù)構(gòu)成。為了節(jié)約時間,這里就借用一張圖來充當(dāng)介紹了

opencv也對此有函數(shù)調(diào)用:
void bilateralFilter( InputArray src, OutputArray dst, int d,
double sigmaColor, double sigmaSpace,
int borderType = BORDER_DEFAULT );前面2個參數(shù)為輸入圖像,輸出圖像,d為雙倍濾波的算子大小,sigmacolor ,sigmaSpace是2個濾波函數(shù)的nameda值(這里節(jié)約時間不打符號了)
本文相關(guān)代碼:
// 雙邊濾波
Mat binateMat;
bilateralFilter(pasteEdgeMat, binateMat, 10, 50, 50, BORDER_DEFAULT);執(zhí)行后的結(jié)果如下:

HSI空間修改飽和度
關(guān)于HSI顏色空間這里就不詳細介紹了,大家可以百度下,很多文章介紹,后續(xù)我也可能總結(jié)一下各個顏色空間,并且與rgb轉(zhuǎn)換方法。主要思路:將貼有邊緣 且 雙邊濾波后的圖像 轉(zhuǎn)化為 HSI 空間,而將S分量增大到原來的SRadio倍,然后將HSI空間圖像轉(zhuǎn)化回Rgb,并顯示。
將顏色空間轉(zhuǎn)化HSI,并增加S分量為原來的sRadio倍,主要是使用了下面這個函數(shù):
// 將image 像素轉(zhuǎn)化到 HSI 空間,并調(diào)整S 即顏色的飽和度,
void changeSImage(Mat &image, IplImage &outImg, float sRadio)
{
int rows = image.rows;
int cols = image.cols;
// 三個HSI空間數(shù)據(jù)矩陣
CvMat* HSI_H = cvCreateMat(rows, cols, CV_32FC1);
CvMat* HSI_S = cvCreateMat(rows, cols, CV_32FC1);
CvMat* HSI_I = cvCreateMat(rows, cols, CV_32FC1);
// 原始圖像數(shù)據(jù)指針, HSI矩陣數(shù)據(jù)指針
uchar* data;
// rgb分量
int img_r, img_g, img_b;
int min_rgb; // rgb分量中的最小值
// HSI分量
float fHue, fSaturation, fIntensity;
int channels = image.channels();
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
data = image.ptr<uchar>(i);
data = data + j*channels;
img_b = *data;
data++;
img_g = *data;
data++;
img_r = *data;
// Intensity分量[0, 1]
fIntensity = (float)((img_b + img_g + img_r) / 3) / 255;
// 得到RGB分量中的最小值
float fTemp = img_r < img_g ? img_r : img_g;
min_rgb = fTemp < img_b ? fTemp : img_b;
// Saturation分量[0, 1]
fSaturation = 1 - (float)(3 * min_rgb) / (img_r + img_g + img_b);
// 計算theta角
float numerator = (img_r - img_g + img_r - img_b) / 2;
float denominator = sqrt(
pow((img_r - img_g), 2) + (img_r - img_b)*(img_g - img_b));
// 計算Hue分量
if (denominator != 0)
{
float theta = acos(numerator / denominator) * 180 / 3.14;
if (img_b <= img_g)
{
fHue = theta;
}
else
{
fHue = 360 - theta;
}
}
else
{
fHue = 0;
}
// 賦值
cvmSet(HSI_H, i, j, fHue);
cvmSet(HSI_S, i, j, fSaturation * sRadio);
cvmSet(HSI_I, i, j, fIntensity);
}
}
outImg = *HSI2RGBImage(HSI_H, HSI_S, HSI_I);
}HSI2RGBImage(HSI_H, HSI_S, HSI_I)是將三個分類的Mat 合并并轉(zhuǎn)化為BGR的圖,函數(shù)如下:
IplImage* HSI2RGBImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I)
{
IplImage * RGB_Image = cvCreateImage(cvGetSize(HSI_H), IPL_DEPTH_8U, 3);
int iB, iG, iR;
for (int i = 0; i < RGB_Image->height; i++)
{
for (int j = 0; j < RGB_Image->width; j++)
{
// 該點的色度H
double dH = cvmGet(HSI_H, i, j);
// 該點的色飽和度S
double dS = cvmGet(HSI_S, i, j);
// 該點的亮度
double dI = cvmGet(HSI_I, i, j);
double dTempB, dTempG, dTempR;
// RG扇區(qū)
if (dH < 120 && dH >= 0)
{
// 將H轉(zhuǎn)為弧度表示
dH = dH * 3.1415926 / 180;
dTempB = dI * (1 - dS);
dTempR = dI * (1 + (dS * cos(dH)) / cos(3.1415926 / 3 - dH));
dTempG = (3 * dI - (dTempR + dTempB));
}
// GB扇區(qū)
else if (dH < 240 && dH >= 120)
{
dH -= 120;
// 將H轉(zhuǎn)為弧度表示
dH = dH * 3.1415926 / 180;
dTempR = dI * (1 - dS);
dTempG = dI * (1 + dS * cos(dH) / cos(3.1415926 / 3 - dH));
dTempB = (3 * dI - (dTempR + dTempG));
}
// BR扇區(qū)
else
{
dH -= 240;
// 將H轉(zhuǎn)為弧度表示
dH = dH * 3.1415926 / 180;
dTempG = dI * (1 - dS);
dTempB = dI * (1 + (dS * cos(dH)) / cos(3.1415926 / 3 - dH));
dTempR = (3 * dI - (dTempG + dTempB));
}
iB = dTempB * 255;
iG = dTempG * 255;
iR = dTempR * 255;
cvSet2D(RGB_Image, i, j, cvScalar(iB, iG, iR));
}
}
return RGB_Image;
}執(zhí)行后就大功告成了,效果如下:

后續(xù):
上述執(zhí)行基本完成了照片的漫畫風(fēng)格,但看到天空的云的一些邊緣泰國刺眼,本著好玩的性子,去掉了第一步和第二步,直接圖原圖進行了雙邊濾波和增加顏色飽和度,感覺圖清晰,自然了些,但漫畫風(fēng)格也少了些,具體如何見下圖:

github地址:https://github.com/hurtnotbad/cartoon
總結(jié)
到此這篇關(guān)于C++ opencv將圖片動漫化介紹的文章就介紹到這了,更多相關(guān)C++ OpenCV圖片動漫化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C/C++細數(shù)宏與函數(shù)有那些區(qū)別
在C程序中,可以用宏代碼提高執(zhí)行效率。宏代碼本身不是函數(shù),但使用起來象函數(shù)。預(yù)處理器用復(fù)制宏代碼的方式代替函數(shù)調(diào)用,省去了參數(shù)壓棧、生成匯編語言的CALL調(diào)用、返回參數(shù)、執(zhí)行return等過程,從而提高了速度2022-10-10
基于Linux系統(tǒng)調(diào)用--getrlimit()與setrlimit()函數(shù)的方法
本篇文章是對在Linux系統(tǒng)中調(diào)用getrlimit()與setrlimit()函數(shù)的方法進行了詳細的分析介紹,需要的朋友參考下2013-05-05
C++編譯錯誤variable-sized?object?may?not?be?initiali問題
這篇文章主要介紹了C++編譯錯誤variable-sized?object?may?not?be?initiali問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05
C++中sort()函數(shù)和priority_queue容器中比較函數(shù)的區(qū)別詳析
C++中sort()和priority_queue都能自定義比較函數(shù),其中sort()自定義的比較函數(shù)比較好理解,priority_queue中自定義的比較函數(shù)的效果和sort()是相反的,這篇文章主要給大家介紹了關(guān)于C++中sort()函數(shù)和priority_queue容器中比較函數(shù)的區(qū)別的相關(guān)資料,需要的朋友可以參考下2023-03-03
StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解
這篇文章主要介紹了StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08

