##基于OpenCV的圖片卡通化處理
學習OpenCV已有一段時間,除了研究各種算法的內容,在空閑之余,根據書本及資料的引導,嘗試結合圖像處理算法和日常生活聯系起來,首先在臺式機上(帶攝像頭)完成一系列視頻流處理功能,開發平臺為Qt5.3.2+OpenCV2.4.9。
本次試驗實現的功能主要有:
1. 調用攝像頭捕獲視頻流;
2. 將幀圖像轉換為素描效果圖片;
3. 將幀圖像卡通化處理;
4. 簡單地生成“怪物”形象;
5. 人臉膚色變換。
本節所有的算法均由類cartoon中的函數cartoonTransform()來實現:
~~~
// Frame:輸入每一幀圖像 output:輸出圖像
cartoonTransform(cv::Mat &Frame, cv::Mat &output)
~~~
后續將使用更多的OpenCV技巧實現更多功能,并將該應用移植到Android系統上。
一、使用OpenCV訪問攝像頭
OpenCV提供了一個簡便易用的框架以提取視頻文件和USB攝像頭中的圖像幀,如果你只是想讀取某個視頻,你只需要創建一個cv::VideoCapture實例,然后在循環中提取每一幀。這里需要訪問攝像頭,因此需要創建一個cv::VideoCapture對象,簡單調用對象的open()方法。這里訪問攝像頭的函數如下,首先在Qt中創建控制臺項目,在main函數中添加:
~~~
int cameraNumber = 0; // 設定攝像頭編號為0
if(argc > 1)
cameraNumber = atoi(argv[1]);
// 開啟攝像頭
cv::VideoCapture camera;
camera.open(cameraNumber);
if(!camera.isOpened())
{
qDebug() << "Error: Could not open the camera.";
exit(1);
}
// 調整攝像頭的輸出分辨率
camera.set(CV_CAP_PROP_FRAME_WIDTH, 640);
camera.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
~~~
在攝像頭被初始化后,可以使用C++流運算符將cv::VideoCapture對象轉換成cv::Mat對象,這樣可以獲取視頻的每一幀圖像。關于視頻流讀取可參考:[http://blog.csdn.net/liyuefeilong/article/details/44066097](http://blog.csdn.net/liyuefeilong/article/details/44066097)
二、將幀圖像轉換為素描效果圖片
要將一幅圖像轉換為素描效果圖,可以使用不同的邊緣檢測算法實現,如常用的基于Sobel、Canny、Robert、Prewitt、Laplacian等算子的濾波器均可以實現這一操作,但處理效果各異。
1.Sobel算子:邊緣檢測中最常用的一種方法,在技術上它是以離散型的差分算子,用來運算圖像亮度函數的梯度的近似值,缺點是Sobel算子并沒有將圖像的主題與背景嚴格地區分開來,換言之就是Sobel算子并沒有基于圖像灰度進行處理,由于Sobel算子并沒有嚴格地模擬人的視覺生理特征,所以提取的圖像輪廓有時并不能令人滿意。
2.Robert算子:根據任一相互垂直方向上的差分都用來估計梯度,Robert算子采用對角方向相鄰像素之差。
3.Prewitt算子:該算子與Sobel算子類似,只是權值有所變化,但兩者實現起來功能還是有差距的,據經驗得知Sobel要比Prewitt更能準確檢測圖像邊緣。
4.Laplacian算子:該算子是一種二階微分算子,若只考慮邊緣點的位置而不考慮周圍的灰度差時可用該算子進行檢測。對于階躍狀邊緣,其二階導數在邊緣點出現零交叉,并且邊緣點兩旁的像素的二階導數異號。
5.Canny算子:該算子的基本性能比前面幾種要好,但是相對來說算法復雜。Canny算子是一個具有濾波,增強,檢測的多階段的優化算子,在進行處理前,Canny算子先利用高斯平滑濾波器來平滑圖像以除去噪聲,Canny分割算法采用一階偏導的有限差分來計算梯度幅值和方向,在處理過程中,Canny算子還將經過一個非極大值抑制的過程,最后Canny算子還采用兩個閾值來連接邊緣。
相比Sobel等其他算子,Canny和Laplacian算子能得到更清晰的素描效果,而Laplacian的噪聲抑制要優于Canny邊緣檢測,而事實上素描邊緣在不同幀之間經常有劇烈的變化,因此我們選擇Laplacian邊緣濾波器進行圖像處理。
一般在進行Laplacian檢測之前,需要對圖像進行的預操作有:
1. Laplacian算法只能作用于灰度圖像,因此需要將彩色幀圖像進行轉換;
2. 平滑處理,這是因為圖像的平滑處理減少了噪聲的影響并且一定成都市抵消了由Laplacian算子的二階導數引起的噪聲影響。因此可使用中值濾波器來去噪。
~~~
void cartoon::cartoonTransform(cv::Mat &Frame, cv::Mat &output)
{
cv::Mat grayImage;
cv::cvtColor(Frame, grayImage, CV_BGR2GRAY);
// 設置中值濾波器參數
cv::medianBlur(grayImage, grayImage, 7);
// Laplacian邊緣檢測
cv::Mat edge; // 用于存放邊緣檢測輸出結果
cv::Laplacian(grayImage, edge, CV_8U, 5);
// 對邊緣檢測結果進行二值化
cv::Mat Binaryzation; // 用于存放二值化輸出結果
cv::threshold(edge, Binaryzation, 80, 255, cv::THRESH_BINARY_INV);
}
~~~
生成的素描效果:

三、將圖像卡通化
在項目中調用一些運算量大的算法時,通常需要考慮到效率問題,比如這里將要用到的雙邊濾波器。這里我們利用雙邊濾波器的平滑區域及保持邊緣銳化的特性,將其運用到卡通圖片效果生成中。而考慮到雙邊濾波器運行效率較低,因此考慮在更低的分辨率中使用,這對效果影響不大,但是運行速度大大加快。
這里使用的策略是將要處理的圖像的寬度和高度縮小為原來的1/2,經過雙邊濾波器處理后,再將其恢復為原來的尺寸。在函數cartoonTransform()中添加以下代碼:
~~~
// 采用雙邊濾波器
// 由于算法復雜,因此需減少圖像尺寸
cv::Size size = Frame.size();
cv::Size reduceSize;
reduceSize.width = size.width / 2;
reduceSize.height = size.height / 2;
cv::Mat reduceImage = cv::Mat(reduceSize, CV_8UC3);
cv::resize(Frame, reduceImage, reduceSize);
// 雙邊濾波器實現過程
cv::Mat tmp = cv::Mat(reduceSize, CV_8UC3);
int repetitions = 7;
for (int i=0 ; i < repetitions; i++)
{
int kernelSize = 9;
double sigmaColor = 9;
double sigmaSpace = 7;
cv::bilateralFilter(reduceImage, tmp, kernelSize, sigmaColor, sigmaSpace);
cv::bilateralFilter(tmp, reduceImage, kernelSize, sigmaColor, sigmaSpace);
}
// 由于圖像是縮小后的圖像,需要恢復
cv::Mat magnifyImage;
cv::resize(reduceImage, magnifyImage, size);
~~~
為了得到更好的效果,在以上代碼中添加以下函數,將恢復尺寸后的圖像與上一部分的素描結果相疊加,得到卡通版的圖像~~
~~~
cv::Mat dst;
dst.setTo(0);
magnifyImage.copyTo(dst, Binaryzation);
//output = dst; //輸出
~~~
卡通效果,閾值各方面有待優化:

四、簡單地生成“怪物”形象
這里是結合了邊緣濾波器和中值濾波器的另一個小應用,即通過小的邊緣濾波器找到圖像中的各處邊緣,之后使用中值濾波器來合并這些邊緣。具體實現步驟如下:
1. 這里同樣需要原圖像的灰度圖,因此格式轉換依然是必要的;
2. 分別沿著x和y方向采用3*3的Scharr梯度濾波器(效果圖);
3. 使用截斷值很低的閾值進行二值化處理;
4. 最后使用3*3的中值平滑濾波得到“怪物”掩碼。
詳細代碼如下,同樣在函數cartoonTransform()中添加:
~~~
// 怪物模式
cv::Mat gray ,maskMonster;
cv::cvtColor(Frame, gray, CV_BGR2GRAY);
// 先對輸入幀進行中值濾波
cv::medianBlur(gray, gray, 7);
// Scharr濾波器
cv::Mat edge1, edge2;
cv::Scharr(gray, edge1, CV_8U, 1, 0);
cv::Scharr(gray, edge2, CV_8U, 1, 0, -1);
edge1 += edge2; // 合并x和y方向的邊緣
cv::threshold(edge1, maskMonster, 12, 255, cv::THRESH_BINARY_INV);
cv::medianBlur(maskMonster, maskMonster, 3);
output = maskMonster; //輸出
~~~

五、人臉膚色變換
皮膚檢測算法有很多種,比如基于RGB color space、Ycrcb之cr分量+otsu閾值化、基于混合模型的復雜機器學習算法等。由于這里只是一個輕量級的應用,因此不考慮使用太復雜的算法。考慮到未來要將這些圖像處理算法移植到安卓上,而移動設備上的微型攝像頭傳感器對顏色的反應往往差異很大,而且要在沒有標定的情況下對不同膚色的人進行皮膚檢測,因此對算法的魯棒性要求較高。
這里使用了一個技巧,即在圖像中規定一個區域,用戶需要將臉部放到指定區域中來確定人臉在圖像中的位置(事實上有些手機應用也會采取這種方法),對于移動設備來說這不是一件難事。
因此,我們需要規定人臉的區域,同樣在函數cartoonTransform()中添加以下代碼:
~~~
// 怪物模式
cv::Mat gray ,maskMonster;
cv::cvtColor(Frame, gray, CV_BGR2GRAY);
// 先對輸入幀進行中值濾波
cv::medianBlur(gray, gray, 7);
// Scharr濾波器
cv::Mat edge1, edge2;
cv::Scharr(gray, edge1, CV_8U, 1, 0);
cv::Scharr(gray, edge2, CV_8U, 1, 0, -1);
edge1 += edge2; // 合并x和y方向的邊緣
cv::threshold(edge1, maskMonster, 12, 255, cv::THRESH_BINARY_INV);
cv::medianBlur(maskMonster, maskMonster, 3);
output = maskMonster; //輸出
// 換膚模式
// 繪制臉部區域
cv::Mat faceFrame = cv::Mat::zeros(size, CV_8UC3);
cv::Scalar color = CV_RGB(128, 0, 128); // 顏色
int thickness = 4;
// 使之占整個圖像高度的70%
int width = size.width;
int height = size.height;
int faceHeight = height/2 * 70/100;
int faceWidth = faceHeight * 72/100;
cv::ellipse(faceFrame, cv::Point(width/2, height/2), cv::Size(faceWidth, faceHeight),
0, 0, 360, color, thickness, CV_AA);
// imshow("test3", faceFrame);
// 繪制眼睛區域
int eyeHeight = faceHeight * 11/100;
int eyeWidth = faceWidth * 23/100;
int eyeY = faceHeight * 13/100;
int eyeX = faceWidth * 48/100;
cv::Size eyeSize = cv::Size(eyeWidth, eyeHeight);
int eyeAngle = 15; //角度
int eyeYShift = 11;
// 畫右眼的上眼皮
cv::ellipse(faceFrame, cv::Point(width/2 - eyeX, height/2 - eyeY),
eyeSize, 0, 180+eyeAngle, 360-eyeAngle, color, thickness, CV_AA);
// 畫右眼的下眼皮
cv::ellipse(faceFrame, cv::Point(width/2 - eyeX, height/2 - eyeY - eyeYShift),
eyeSize, 0, 0+eyeAngle, 180-eyeAngle, color, thickness, CV_AA);
// 畫左眼的上眼皮
cv::ellipse(faceFrame, cv::Point(width/2 + eyeX, height/2 - eyeY),
eyeSize, 0, 180+eyeAngle, 360-eyeAngle, color, thickness, CV_AA);
// 畫左眼的下眼皮
cv::ellipse(faceFrame, cv::Point(width/2 + eyeX, height/2 - eyeY - eyeYShift),
eyeSize, 0, 0+eyeAngle, 180-eyeAngle, color, thickness, CV_AA);
char *Message = "Put your face here";
cv::putText(faceFrame, Message, cv::Point(width * 13/100, height * 10/100),
cv::FONT_HERSHEY_COMPLEX,
1.0f,
color,
2,
CV_AA);
cv::addWeighted(dst, 1.0, faceFrame, 0.7, 0, dst, CV_8UC3);
//output = dst;
~~~
效果:

皮膚變色器的實現基于OpenCV的floodFill()函數,該函數類似于一些繪圖軟件中的**顏料桶(顏色填充)工具**。 由于規定屏幕中間橢圓區域就是皮膚像素,因此只需要對該區域的像素進行各種顏色的漫水填充即可。
這里處理的圖像是彩色圖,而對于RGB格式的圖像,改變顏色的效果不會太好,因為改變顏色需要臉部圖像的亮度變化,而皮膚顏色也不能變化太大。這里使用YCrCb顏色空間來進行處理。在YCrCb顏色空間中,可以直接獲得亮度值,而且通常的皮膚顏色取值唯一。
~~~
// 皮膚變色器
cv::Mat YUVImage = cv::Mat(reduceSize, CV_8UC3);
cv::cvtColor(reduceImage, YUVImage, CV_BGR2YCrCb);
int sw = reduceSize.width;
int sh = reduceSize.height;
cv::Mat mask, maskPlusBorder;
maskPlusBorder = cv::Mat::zeros(sh+2, sw+2, CV_8UC1);
mask = maskPlusBorder(cv::Rect(1, 1, sw, sh));
cv::resize(edge, mask, reduceSize);
const int EDGES_THRESHOLD = 80;
cv::threshold(mask, mask, EDGES_THRESHOLD, 255, cv::THRESH_BINARY);
cv::dilate(mask, mask, cv::Mat());
cv::erode(mask, mask, cv::Mat());
// output = mask;
// 創建6個點進行漫水填充算法
cv::Point skinPoint[6];
skinPoint[0] = cv::Point(sw/2, sh/2 - sh/6);
skinPoint[1] = cv::Point(sw/2 - sw/11, sh/2 - sh/6);
skinPoint[2] = cv::Point(sw/2 + sw/11, sh/2 - sh/6);
skinPoint[3] = cv::Point(sw/2, sh/2 + sh/6);
skinPoint[4] = cv::Point(sw/2 - sw/9, sh/2 + sh/6);
skinPoint[5] = cv::Point(sw/2 + sw/9, sh/2 + sh/6);
// 設定漫水填充算法的上下限
const int MIN_Y = 60;
const int MAX_Y = 80;
const int MIN_Cr = 25;
const int MAX_Cr = 15;
const int MIN_Cb = 20;
const int MAX_Cb = 15;
cv::Scalar Min = cv::Scalar(MIN_Y, MIN_Cr, MIN_Cb);
cv::Scalar Max = cv::Scalar(MAX_Y, MAX_Cr, MAX_Cb);
// 調用漫水填充算法
const int CONNECTED_COMPONENTS = 4;
const int flag = CONNECTED_COMPONENTS | cv::FLOODFILL_FIXED_RANGE \
| cv::FLOODFILL_MASK_ONLY;
cv::Mat edgeMask = mask.clone();
//
for(int i = 0; i < 6; i++)
{
cv::floodFill(YUVImage, maskPlusBorder, skinPoint[i], cv::Scalar(), NULL,
Min, Max, flag);
}
cv::Mat BGRImage;
cv::cvtColor(YUVImage, BGRImage, CV_YCrCb2BGR);
mask -= edgeMask;
int Red = 0;
int Green = 70;
int Blue = 0;
cv::Scalar color2 = CV_RGB(Red, Green, Blue); // 顏色
cv::add(BGRImage, color2, BGRImage, mask);
cv::Mat tt;
cv::resize(BGRImage, tt, size);
cv::add(dst, tt ,dst);
output = dst; // 換膚結果
~~~
由于在臉部區域中要對許多像素使用漫水填充算法,因此為了保證人臉圖像的各種顏色和陰影都能得到處理,這里設置了前額、鼻子和臉頰6個點,他們的定位依賴于先前規定的臉部輪廓坐標。輸出效果如下:
臉部不在識別區域內時:

臉部進入識別區域內時:

以上實現了幾種圖片卡通化效果,接著在學有余力時要對各種算法的效果進行優化,同時加入GUI界面,并將應用移植到移動設備上。
參考資料:《深入理解OpenCV:實用計算機視覺項目解析》
完整代碼:
cartoon.h:
~~~
#ifndef CARTOON_H
#define CARTOON_H
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
class cartoon
{
public:
void cartoonTransform(cv::Mat &Frame, cv::Mat &output);
};
#endif // CARTOON_H
~~~
cartoon.cpp:
~~~
#include "cartoon.h"
void cartoon::cartoonTransform(cv::Mat &Frame, cv::Mat &output)
{
cv::Mat grayImage;
cv::cvtColor(Frame, grayImage, CV_BGR2GRAY);
// 設置中值濾波器參數
cv::medianBlur(grayImage, grayImage, 7);
// Laplacian邊緣檢測
cv::Mat edge; // 用于存放邊緣檢測輸出結果
cv::Laplacian(grayImage, edge, CV_8U, 5);
// 對邊緣檢測結果進行二值化
cv::Mat Binaryzation; // 用于存放二值化輸出結果
cv::threshold(edge, Binaryzation, 80, 255, cv::THRESH_BINARY_INV);
// 以下操作生成彩色圖像和卡通效果
// 采用雙邊濾波器
// 由于算法復雜,因此需減少圖像尺寸
cv::Size size = Frame.size();
cv::Size reduceSize;
reduceSize.width = size.width / 2;
reduceSize.height = size.height / 2;
cv::Mat reduceImage = cv::Mat(reduceSize, CV_8UC3);
cv::resize(Frame, reduceImage, reduceSize);
// 雙邊濾波器實現過程
cv::Mat tmp = cv::Mat(reduceSize, CV_8UC3);
int repetitions = 7;
for (int i=0 ; i < repetitions; i++)
{
int kernelSize = 9;
double sigmaColor = 9;
double sigmaSpace = 7;
cv::bilateralFilter(reduceImage, tmp, kernelSize, sigmaColor, sigmaSpace);
cv::bilateralFilter(tmp, reduceImage, kernelSize, sigmaColor, sigmaSpace);
}
// 由于圖像是縮小后的圖像,需要恢復
cv::Mat magnifyImage;
cv::resize(reduceImage, magnifyImage, size);
cv::Mat dst;
dst.setTo(0);
magnifyImage.copyTo(dst, Binaryzation);
//output = dst; //輸出
// 怪物模式
cv::Mat gray ,maskMonster;
cv::cvtColor(Frame, gray, CV_BGR2GRAY);
// 先對輸入幀進行中值濾波
cv::medianBlur(gray, gray, 7);
// Scharr濾波器
cv::Mat edge1, edge2;
cv::Scharr(gray, edge1, CV_8U, 1, 0);
cv::Scharr(gray, edge2, CV_8U, 1, 0, -1);
edge1 += edge2; // 合并x和y方向的邊緣
cv::threshold(edge1, maskMonster, 12, 255, cv::THRESH_BINARY_INV);
cv::medianBlur(maskMonster, maskMonster, 3);
output = maskMonster; //輸出
// 換膚模式
// 繪制臉部區域
cv::Mat faceFrame = cv::Mat::zeros(size, CV_8UC3);
cv::Scalar color = CV_RGB(128, 0, 128); // 顏色
int thickness = 4;
// 使之占整個圖像高度的70%
int width = size.width;
int height = size.height;
int faceHeight = height/2 * 70/100;
int faceWidth = faceHeight * 72/100;
cv::ellipse(faceFrame, cv::Point(width/2, height/2), cv::Size(faceWidth, faceHeight),
0, 0, 360, color, thickness, CV_AA);
// imshow("test3", faceFrame);
// 繪制眼睛區域
int eyeHeight = faceHeight * 11/100;
int eyeWidth = faceWidth * 23/100;
int eyeY = faceHeight * 13/100;
int eyeX = faceWidth * 48/100;
cv::Size eyeSize = cv::Size(eyeWidth, eyeHeight);
int eyeAngle = 15; //角度
int eyeYShift = 11;
// 畫右眼的上眼皮
cv::ellipse(faceFrame, cv::Point(width/2 - eyeX, height/2 - eyeY),
eyeSize, 0, 180+eyeAngle, 360-eyeAngle, color, thickness, CV_AA);
// 畫右眼的下眼皮
cv::ellipse(faceFrame, cv::Point(width/2 - eyeX, height/2 - eyeY - eyeYShift),
eyeSize, 0, 0+eyeAngle, 180-eyeAngle, color, thickness, CV_AA);
// 畫左眼的上眼皮
cv::ellipse(faceFrame, cv::Point(width/2 + eyeX, height/2 - eyeY),
eyeSize, 0, 180+eyeAngle, 360-eyeAngle, color, thickness, CV_AA);
// 畫左眼的下眼皮
cv::ellipse(faceFrame, cv::Point(width/2 + eyeX, height/2 - eyeY - eyeYShift),
eyeSize, 0, 0+eyeAngle, 180-eyeAngle, color, thickness, CV_AA);
char *Message = "Put your face here";
cv::putText(faceFrame, Message, cv::Point(width * 13/100, height * 10/100),
cv::FONT_HERSHEY_COMPLEX,
1.0f,
color,
2,
CV_AA);
cv::addWeighted(dst, 1.0, faceFrame, 0.7, 0, dst, CV_8UC3);
//output = dst;
// 皮膚變色器
cv::Mat YUVImage = cv::Mat(reduceSize, CV_8UC3);
cv::cvtColor(reduceImage, YUVImage, CV_BGR2YCrCb);
int sw = reduceSize.width;
int sh = reduceSize.height;
cv::Mat mask, maskPlusBorder;
maskPlusBorder = cv::Mat::zeros(sh+2, sw+2, CV_8UC1);
mask = maskPlusBorder(cv::Rect(1, 1, sw, sh));
cv::resize(edge, mask, reduceSize);
const int EDGES_THRESHOLD = 80;
cv::threshold(mask, mask, EDGES_THRESHOLD, 255, cv::THRESH_BINARY);
cv::dilate(mask, mask, cv::Mat());
cv::erode(mask, mask, cv::Mat());
// output = mask;
// 創建6個點進行漫水填充算法
cv::Point skinPoint[6];
skinPoint[0] = cv::Point(sw/2, sh/2 - sh/6);
skinPoint[1] = cv::Point(sw/2 - sw/11, sh/2 - sh/6);
skinPoint[2] = cv::Point(sw/2 + sw/11, sh/2 - sh/6);
skinPoint[3] = cv::Point(sw/2, sh/2 + sh/6);
skinPoint[4] = cv::Point(sw/2 - sw/9, sh/2 + sh/6);
skinPoint[5] = cv::Point(sw/2 + sw/9, sh/2 + sh/6);
// 設定漫水填充算法的上下限
const int MIN_Y = 60;
const int MAX_Y = 80;
const int MIN_Cr = 25;
const int MAX_Cr = 15;
const int MIN_Cb = 20;
const int MAX_Cb = 15;
cv::Scalar Min = cv::Scalar(MIN_Y, MIN_Cr, MIN_Cb);
cv::Scalar Max = cv::Scalar(MAX_Y, MAX_Cr, MAX_Cb);
// 調用漫水填充算法
const int CONNECTED_COMPONENTS = 4;
const int flag = CONNECTED_COMPONENTS | cv::FLOODFILL_FIXED_RANGE \
| cv::FLOODFILL_MASK_ONLY;
cv::Mat edgeMask = mask.clone();
//
for(int i = 0; i < 6; i++)
{
cv::floodFill(YUVImage, maskPlusBorder, skinPoint[i], cv::Scalar(), NULL,
Min, Max, flag);
}
cv::Mat BGRImage;
cv::cvtColor(YUVImage, BGRImage, CV_YCrCb2BGR);
mask -= edgeMask;
int Red = 0;
int Green = 70;
int Blue = 0;
cv::Scalar color2 = CV_RGB(Red, Green, Blue); // 顏色
cv::add(BGRImage, color2, BGRImage, mask);
cv::Mat tt;
cv::resize(BGRImage, tt, size);
cv::add(dst, tt ,dst);
output = dst; // 換膚結果
}
~~~
main函數:
~~~
#include "cartoon.h"
#include <QApplication>
#include <QDebug>
#include <opencv2/video/video.hpp>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
cartoon photo;
int cameraNumber = 0;
if(argc > 1)
cameraNumber = atoi(argv[1]);
// 開啟攝像頭
cv::VideoCapture camera;
camera.open(cameraNumber);
if(!camera.isOpened())
{
qDebug() << "Error: Could not open the camera.";
exit(1);
}
// 調整攝像頭的分辨率
camera.set(CV_CAP_PROP_FRAME_WIDTH, 640);
camera.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
while (1)
{
cv::Mat Frame;
camera >> Frame;
if(!Frame.data)
{
qDebug() << "Couldn't capture camera frame.";
exit(1);
}
// 創建一個用于存放輸出圖像的數據結構
cv::Mat output(Frame.size(), CV_8UC3);
photo.cartoonTransform(Frame, output);
// 使用圖像處理技術將獲取的幀經過處理后輸入到output中
cv::imshow("Original", Frame);
cv::imshow("Carton", output);
char keypress = cv::waitKey(20);
if(keypress == 27)
{
break;
}
}
return a.exec();
}
~~~
- 前言
- Win8.1下OpenCV2.4.9+Qt5.3.2開發環境搭建
- OpenCV2學習筆記(一)
- OpenCV2學習筆記(二)
- OpenCV2學習筆記(三)
- OpenCV2學習筆記(四)
- OpenCV2學習筆記(五)
- OpenCV2學習筆記(六)
- OpenCV2學習筆記(七)
- OpenCV2學習筆記(八)
- OpenCV2學習筆記(九)
- OpenCV2學習筆記(十)
- OpenCV2學習筆記(十一)
- OpenCV2學習筆記(十二)
- OpenCV2學習筆記(十三)
- OpenCV2學習筆記(十四)
- OpenCV2學習筆記(十五)
- OpenCV2學習筆記(十六)
- OpenCV2學習筆記(十七)
- OpenCV2學習筆記(十八)
- OpenCV2學習筆記(十九)
- OpenCV2學習筆記(二十)
- OpenCV2學習筆記(二十一)
- OpenCV2學習筆記(二十二)