博主聯系方式:
QQ:1540984562
微信:wxid_nz49532kbh9u22 QQ交流群:750313950
目錄
- 動漫化風格的特點
- 處理手段
- 代碼
- 實現效果
- 總結
動漫化風格的特點
(1)動漫中的細節相對少;
(2)動漫中的邊緣輪廓更突出;
(3)動漫的色彩更鮮艷;
處理手段
(1)突出邊緣線條
利用canny算子找出邊緣,然后利用copyTo函數將邊緣加到原圖上。
這里用到copyTo函數的第二種用法:
copyTo(roi , mask)
作用是把mask和image重疊以后把mask中像素值為非0(black)的點對應的image中的點變為透明,而保留為0的點。
然而我們發現,canny算子得到的結果是二值化圖,其中邊緣像素值為255,是白色,非邊緣像素是0,是黑色。
若是直接將canny算子得到的結果進行累加,則會是這樣的結果。
所以我們要進行處理:
change_g_cannyImage = 255 - g_cannyImage;
非邊緣轉化為255,邊緣轉化為0;非邊緣會在之后的處理會變為透明,而邊緣則會保持原有的數據0,為黑色,這樣就會有強化邊緣的效果。
主要代碼:
//【1】運行canny算子Canny(g_grayImage, g_cannyImage, g_nThresholdValue, g_nThresholdValue/3, 3);cv::Mat g_canny3Image(g_srcImage.rows, g_srcImage.cols, CV_8UC3, cv::Scalar(0,0,0));//【2】貼圖//(將邊緣變為黑色)Mat change_g_cannyImage;change_g_cannyImage = 255 - g_cannyImage;//將單通道轉化為三通道cvtColor(change_g_cannyImage, g_canny3Image, COLOR_GRAY2BGR);//image.copyTo(imageROI,mask), 作用是把mask和image重疊以后把mask中像素值為0(black)的點對應的image中的點變為透明,而保留其他點。Mat bianyuan_dst;g_srcImage.copyTo(bianyuan_dst, g_canny3Image);
效果:
(2)弱化與去除細節
這里的細節是除了邊緣外的,只在區域的內部進行弱化。這時就需要使用雙邊濾波。
所謂“細節”,從圖像處理的角度看來就是圖像中的高頻成分。要想去除高頻成分,自然而然就要用到濾波(filtering)的方法。常用的濾波器有均值濾波器、高斯濾波器、中值濾波器等。但是,這些常用濾波器都有一個共同的問題——會弱化所有的高頻信息。而很不幸的是,圖像中的邊緣也屬于高頻信息(因為邊緣意味著圖像在這里產生了突變,突變就意味著高頻)。因此常用濾波器會將我們本應突出的邊緣一起弱化模糊。
這種情況下就要讓雙邊濾波器(Bilateral filter)出場了。這種濾波器的特點是可以“保邊濾波”(或者叫“區域平滑”,Region smoothing)。顧名思義,就是可以只模糊區域內部而保留清晰的邊緣。為了搞明白雙邊濾波器為什么有這樣的效果,首先來說一下高斯濾波器。高斯濾波器,或者說高斯濾波模板,其中的各個點的值僅與該點到模板中心點的空間距離有關,而并沒有考慮各個點與中心點的相似度(即像素值的接近程度),這樣就導致無論是變化不大的區域內部點,還是突變的邊緣點,只要和中心的距離相同,那就同等對待。
而雙邊濾波器就是在高斯濾波器基礎上加上了相似度權重,在高斯濾波模板的每個點上再乘以一個與中心點的相似度系數(即“相似度權重”),從而將邊緣與內部區分處理。相似度權重計算方法和高斯濾波模板中各點值(可以稱為“高斯權重”)的計算方法相同,只不過高斯權重是將該點到中心的距離代入高斯函數計算,而相似度權重是將該點與中心的像素相似度(比如該點像素值與中心像素值的歐氏距離,或者直接求二者的差值)代入高斯函數計算得到。
//【3】雙邊濾波Mat lvbo_dst;bilateralFilter(bianyuan_dst, lvbo_dst, g_nkernelValue, g_nkernelValue * 2, g_nkernelValue / 2);
(3)讓圖像色彩更鮮艷
提高色彩飽和度。
方法:
1、RGB轉HSV,且提取原圖像的S通道
2、乘上一個大于1的數,并且對值進行限幅
3、將修改后的S通道替換掉原本的S通道,并將3個通道合并
4、HSV轉為RGB
//【4】修改圖像的顏色的飽和度Mat hsv_image,hsv_dst;cvtColor(lvbo_dst, hsv_image, COLOR_BGR2HSV);vector<Mat> channels;split(hsv_image, channels);Mat S_Mat;float k = g_nS*1.0f / 100;channels.at(1).copyTo(S_Mat);cv::Mat S_dst(S_Mat.rows, S_Mat.cols, CV_8UC1, cv::Scalar(0));//S_dst = S_Mat * k;H_mul_k(&S_Mat, &S_dst,k);//將修改后的S與原來的H,V進行mergechannels[1] = S_dst.clone(); //深復制merge(channels, hsv_dst);//將修改后的HSV轉為RGB圖Mat RGB_dst;cvtColor(hsv_dst, RGB_dst, COLOR_HSV2BGR);
代碼
#include <opencv2/opencv.hpp>
#include "opencv2/features2d.hpp"
#include <iostream>
#include "windows.h"
#include <stdio.h>
#include <time.h>
#include <math.h>
#define WINDOW_NAME "【程序窗口】"
using namespace cv;
using namespace std;
//RNG g_rng(12345);//對照片進行動漫化一般需要四個步驟
//1、邊緣檢測
//2、將邊緣檢測得到的邊緣 以黑色的形式貼在原來的畫上。
//3、對貼了邊緣的圖進行雙邊濾波,雙邊濾波可以較好的濾波的同時保留邊緣。
//4、修改圖像的顏色的飽和度,本文采用的是將RGB轉化為HSV空間,然后調整S分量。//*--------------------------【全局變量聲明】-------------------------------------*/
int g_nThresholdValue = 100; //canny參數值
int g_nkernelValue = 3; //雙邊濾波核大小
int g_nS = 100; //雙邊濾波核大小
Mat g_srcImage, g_grayImage,g_cannyImage,g_dstImage;//*--------------------------【全局函數聲明】-------------------------------------*/
void on_CannyThreshold(int, void*); //回調函數/****照片動漫化示例**********/
int main()
{//載入原圖g_srcImage = imread("D:\\opencv_picture_test\\HOG行人檢測\\timg.jpg");//加載原圖if (g_srcImage.empty()){printf("Could not find the image!\n");return -1;}g_grayImage.create(g_srcImage.size(), g_srcImage.type()); //創建一個同大小類型的矩陣cvtColor(g_srcImage, g_grayImage,COLOR_BGR2GRAY);//imshow("【原圖的灰度圖】", g_grayImage);//進行均值濾波操作blur(g_grayImage, g_grayImage, Size(3, 3));namedWindow(WINDOW_NAME, WINDOW_NORMAL);//WINDOW_NORMAL允許用戶自由伸縮窗口//【3】創建窗口 并 顯示原圖//imshow("原始圖", g_srcImage);//【4】創建滑動條來控制閾值createTrackbar("canny參數值", WINDOW_NAME, &g_nThresholdValue, 255, on_CannyThreshold);createTrackbar("雙邊濾波核", WINDOW_NAME, &g_nkernelValue, 25, on_CannyThreshold);createTrackbar("S擴大", WINDOW_NAME, &g_nS, 300, on_CannyThreshold);//【5】初始化自定義的閾值回調函數on_CannyThreshold(0, 0);while (1){if (waitKey(10) == 27) break; //按下Esc 退出}return 0;}void H_mul_k(Mat* srcImage, Mat* dstImage, float k)
{int height = (*srcImage).rows;int width = (*srcImage).cols;for (int j = 0; j < height; j++){for (int i = 0; i < width; i++){int zhi = (*srcImage).at<uchar>(j, i) * k;if (zhi >= 255) zhi = 255;else if (zhi <= 0) zhi = 0;else zhi = zhi;(*dstImage).at<uchar>(j, i) = zhi;}}
}
//*--------------------------【on_Threshold 函數】-------------------------------------*/
void on_CannyThreshold(int, void*)
{//【1】運行canny算子Canny(g_grayImage, g_cannyImage, g_nThresholdValue, g_nThresholdValue/3, 3);cv::Mat g_canny3Image(g_srcImage.rows, g_srcImage.cols, CV_8UC3, cv::Scalar(0,0,0));//【2】貼圖//將canny圖反轉(將邊緣變為黑色)Mat change_g_cannyImage;//change_g_cannyImage = g_cannyImage < 100; //非邊緣轉化為255,邊緣轉化為0;非邊緣會在之后的處理會變為透明,而邊緣則會保持原有的數據0change_g_cannyImage = 255 - g_cannyImage;//將單通道轉化為三通道cvtColor(change_g_cannyImage, g_canny3Image, COLOR_GRAY2BGR);//image.copyTo(imageROI,mask), 作用是把mask和image重疊以后把mask中像素值為0(black)的點對應的image中的點變為透明,而保留其他點。Mat bianyuan_dst;g_srcImage.copyTo(bianyuan_dst, g_canny3Image);//【3】雙邊濾波Mat lvbo_dst;bilateralFilter(bianyuan_dst, lvbo_dst, g_nkernelValue, g_nkernelValue * 2, g_nkernelValue / 2);//【4】修改圖像的顏色的飽和度Mat hsv_image,hsv_dst;cvtColor(lvbo_dst, hsv_image, COLOR_BGR2HSV);vector<Mat> channels;split(hsv_image, channels);Mat S_Mat;float k = g_nS*1.0f / 100;channels.at(1).copyTo(S_Mat);cv::Mat S_dst(S_Mat.rows, S_Mat.cols, CV_8UC1, cv::Scalar(0));//S_dst = S_Mat * k;H_mul_k(&S_Mat, &S_dst,k);//將修改后的S與原來的H,V進行mergechannels[1] = S_dst.clone(); //深復制merge(channels, hsv_dst);//將修改后的HSV轉為RGB圖Mat RGB_dst;cvtColor(hsv_dst, RGB_dst, COLOR_HSV2BGR);imshow(WINDOW_NAME, RGB_dst);
}
實現效果
總結
這種方法其實只能對部分景象圖有比較好的漫畫效果,對人的臉部面容效果其實并不是很好。
似乎是有根據深度學習訓練出來的算法,能夠對人臉面容實現很好的動漫化:
如何用深度學習模型為自己做個漫畫畫像
零門檻人像轉卡通、GIF表情包
這兩個鏈接并沒有相關代碼,原理還是很復雜的,以后再進行研究。
Reference:
圖片動漫化處理原理
opencv 照片動漫風格
RGB、HSV、HSI顏色空間
OpenCV cvtColor()函數
opencv中的merge函數
openCv——copyTo()的形式詳解