【opencv實戰】哈哈鏡
目錄
因為要做一個專案,為了實現他的趣味性,所以想應用影象處理做一些東西,在上次完成卡通化之後,又瞭解了哈哈鏡效果,想自己實現,從網上找了好多教程,都是以前的opencv版本的程式碼,在opencv3.0及以上版本已經不支援使用了。
可能最新學習opencv的小夥伴不瞭解什麼是“以前的opencv版本”。所以我先簡單介紹一下。
一、opencv的前身後世
1、簡介
OpenCV是一個基於BSD許可(開源)發行的跨平臺計算機視覺庫,可以執行在Linux、Windows、Android和Mac OS作業系統上。它輕量級而且高效——由一系列 C 函式和少量 C++ 類構成,同時提供了Python、Ruby、MATLAB等語言的介面,實現了影象處理和計算機視覺方面的很多通用演算法。
OpenCV用C++語言編寫,它的主要介面也是C++語言,但是依然保留了大量的C語言介面。所有新的開發和演算法都是用C++介面。一個使用CUDA的GPU介面也於2010年9月開始實現。
其他的一些介紹就在這裡不多說了,大家在網上也能找到。我主要再說一下在前面我說到的以前的opencv版本和新版本的差別。這個差別不是opencv2.0,opencv2.3.4,opencv3.0.0,opencv3.1.0,opencv3.4.0等等這些版本之間的差別。大家會發現,大家現在在學習opencv時,建立影象,用的時C++語言中的Mat類,最初的opencv是用C語言編寫的,C語言是沒有類的,那用C語言用的自然就是結構體。所以接下來我講一下opencv結構體的表示。
2、IplImage介紹
在OpenCV中IplImage是表示一個影象的結構體,也是從OpenCV1.0到目前最為重要的一個結構;在之前的影象表示用IplImage,而且之前的OpenCV是用C語言編寫的,提供的介面也是C語言介面;
英文註釋版結構體如下:
typedef struct _IplImage
{
int nSize; /* sizeof(IplImage) */
int ID; /* version (=0)*/
int nChannels; /* Most of OpenCV functions support 1,2,3 or 4 channels */
int alphaChannel; /* Ignored by OpenCV */
int depth; /* Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,
IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported. */
char colorModel[4]; /* Ignored by OpenCV */
char channelSeq[4]; /* ditto */
int dataOrder; /* 0 - interleaved color channels, 1 - separate color channels.
cvCreateImage can only create interleaved images */
int origin; /* 0 - top-left origin,
1 - bottom-left origin (Windows bitmaps style). */
int align; /* Alignment of image rows (4 or 8).
OpenCV ignores it and uses widthStep instead. */
int width; /* Image width in pixels. */
int height; /* Image height in pixels. */
struct _IplROI *roi; /* Image ROI. If NULL, the whole image is selected. */
struct _IplImage *maskROI; /* Must be NULL. */
void *imageId; /* " " */
struct _IplTileInfo *tileInfo; /* " " */
int imageSize; /* Image data size in bytes
(==image->height*image->widthStep
in case of interleaved data)*/
char *imageData; /* Pointer to aligned image data. */
int widthStep; /* Size of aligned image row in bytes. */
int BorderMode[4]; /* Ignored by OpenCV. */
int BorderConst[4]; /* Ditto. */
char *imageDataOrigin; /* Pointer to very origin of image data
(not necessarily aligned) -
needed for correct deallocation */
}
IplImage;
中文註釋版結構體如下:
typedef struct _IplImage
{
int nSize; /* IplImage大小 */
int ID; /* 版本 (=0)*/
int nChannels; /* 大多數OPENCV函式支援1,2,3 或 4 個通道 */
int alphaChannel; /* 被OpenCV忽略 */
int depth; /* 畫素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支援 */
char colorModel[4]; /* 被OpenCV忽略 */
char channelSeq[4]; /* 同上 */
int dataOrder; /* 0 - 交叉存取顏色通道, 1 - 分開的顏色通道.
cvCreateImage只能建立交叉存取影象 */
int origin; /* 0 - 頂—左結構,
1 - 底—左結構 (Windows bitmaps 風格) */
int align; /* 影象行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
int width; /* 影象寬畫素數 */
int height; /* 影象高畫素數*/
struct _IplROI *roi;/* 影象感興趣區域. 當該值非空只對該區域進行處理 */
struct _IplImage *maskROI; /* 在 OpenCV中必須置NULL */
void *imageId; /* 同上*/
struct _IplTileInfo *tileInfo; /*同上*/
int imageSize; /* 影象資料大小(在交叉存取格式下imageSize=image->height*image->widthStep),單位位元組*/
char *imageData; /* 指向排列的影象資料 */
int widthStep; /* 排列的影象行大小,以位元組為單位 */
int BorderMode[4]; /* 邊際結束模式, 被OpenCV忽略 */
int BorderConst[4]; /* 同上 */
char *imageDataOrigin; /* 指標指向一個不同的影象資料結構(不是必須排列的),是為了糾正影象記憶體分配準備的 */
}
IplImage;
IplImage結構體是整個OpenCV函式庫的基礎,在定義該結構變數時需要用到函式cvCreatImage,變數定義方法如下:
//定義一個IplImage指標變數src,影象的大小是200×300,影象顏色深度8位,3通道影象。
IplImage* src = "/cvCreateImage"(cvSize(200, 300), IPL_DEPTH_8U, 3);
//定義一個IplImage指標變數src,影象的大小是200×300,影象顏色深度8位,單通道影象。
IplImage* src = "/cvCreateImage"(cvSize(200, 300), IPL_DEPTH_8U, 1);
由於定義的src是一個指標變數,所以通過src來呼叫函式時,採用的是指向的方式:
//下面是兩種影象資料存取方式的例子:
//1.直接存取 : (效率高, 但容易出錯)
// 對單通道位元組影象 :
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 1);
((uchar *)(src->imageData + i*src->widthStep))[j] = 111;
// 對多通道位元組影象:
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3);
((uchar *)(src->imageData + i*src->widthStep))[j*src->nChannels + 0] = 111; // B
((uchar *)(src->imageData + i*src->widthStep))[j*src->nChannels + 1] = 112; // G
((uchar *)(src->imageData + i*src->widthStep))[j*src->nChannels + 2] = 113; // R
// 對多通道浮點影象:
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_32F, 3);
((float *)(src->imageData + i*src->widthStep))[j*src->nChannels + 0] = 111; // B
((float *)(src->imageData + i*src->widthStep))[j*src->nChannels + 1] = 112; // G
((float *)(src->imageData + i*src->widthStep))[j*src->nChannels + 2] = 113; // R
//2.用指標直接存取 : (在某些情況下簡單高效)
// 對單通道位元組影象 :
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 1);
int height = src->height;
int width = src->width;
int step = src->widthStep / sizeof(uchar);
uchar* data = (uchar *)src->imageData;
data[i*step + j] = 111;
// 對多通道位元組影象:
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3);
int height = src->height;
int width = src->width;
int step = src->widthStep / sizeof(uchar);
int channels = src->nChannels;
uchar* data = (uchar *)src->imageData;
data[i*step + j*channels + k] = 111;
// 對多通道浮點影象(假設用4位元組調整) :
IplImage* src = cvCreateImage(cvSize(640, 480), IPL_DEPTH_32F, 3);
int height = src->height;
int width = src->width;
int step = src->widthStep / sizeof(float);
int channels = src->nChannels;
float * data = (float *)src->imageData;
data[i*step + j*channels + k] = 111;
3、Mat介紹
具體介紹內容詳解我的:【opencv學習筆記】004之Mat物件 。裡面有Mat簡介,常用成員,建構函式以及三種影象型別格式的轉換。
二、哈哈鏡介紹
1、原理
哈哈鏡是表面凸凹不平的鏡面,反映人像及物件的扭曲面貌,令人發笑,故名叫哈哈鏡。哈哈鏡的原理是曲面鏡引起的不規則光線反射與聚焦,做成散亂的影像。鏡面扭曲的情況不同,成像的效果也會相異。 常見的變換效果有高矮胖瘦四種效果,鏡面材質有金屬哈哈鏡,玻璃哈哈鏡等。
對應到物理中,哈哈鏡其實是光的折射,可以理解為數學中的對映,不同的對映會有不同的效果,如線性對映會產生放大縮小的感覺,凸函式則會是凸透鏡,凹函式就是凹透鏡,原則上,不同的函式就不會產生不同的結果。
所以如果希望通過opencv來做哈哈鏡,就需要找到一個對應的對映,讓影象的畫素扭曲,從而實現哈哈效果。在這裡,實現了放大鏡和縮小鏡。
2、實現
我希望實現的是實時將視訊影象卡通化,所以需要通過opencv呼叫攝像頭,並對其進行一系列設定。在這裡,採用了最簡單的呼叫攝像頭的方式:
VideoCapture capture;
capture.open(0);
獲取到每一幀的影象後,需要對影象做一定的處理,因為用了兩種方式做處理分別得到:放大鏡,縮小鏡。所以在處理之前加一個整形變數,允許使用者輸入,自由選擇處理方式,為了防止使用者非法輸入,我設定迴圈做判斷。輸入合法後才允許執行下面的程式碼。並通過Switch語句設定兩種處理方式。程式碼如下:
int mode = -1;//動畫處理模式
cout << "請輸入型別:";
cin >> mode;
while (mode<0 || mode >= 2)
{
cout << "處理模式輸入錯誤,請重新輸入:";
cin >> mode;
}
switch (mode)
{
case 0:
while (1)
{
capture >> hahaFrame;
hahaFrame.copyTo(img);
magnifyGlass(hahaFrame,img);
imshow("【放大鏡】", img);
waitKey(30);
}
break;
case 1:
while (1)
{
capture >> hahaFrame;
hahaFrame.copyTo(img);
compressGlass(hahaFrame,img);
imshow("【壓縮鏡】", img);
waitKey(30);
}
break;
default:
break;
}
接下來就是最核心的演算法,即對映了。
在前面我們說到,所謂哈哈鏡,就是影象畫素點位置的變化,所以我們要獲取到每個畫素點的畫素值,然後對畫素點做操作。
3、凸透鏡演算法
void magnifyGlass(Mat hahaFrame,Mat img) {
//【1】凸透鏡
int width = hahaFrame.cols;
int heigh = hahaFrame.rows;
Point center(width / 2, heigh / 2);
int R = sqrtf(width*width + heigh*heigh) / 2; //直接關係到放大的力度,與R成正比;
for (int y = 0; y < heigh; y++)
{
uchar *img_p = img.ptr<uchar>(y);//定義一個指標,指向第y列,從而可以訪問行資料。
for (int x = 0; x < width; x++)
{
int dis = norm(Point(x, y) - center);//獲得當前點到中心點的距離
if (dis < R)//設定變化區間
{
int newX = (x - center.x)*dis / R + center.x;
int newY = (y - center.y)*dis / R + center.y;
img_p[3 * x] = hahaFrame.at<uchar>(newY, newX * 3);
img_p[3 * x + 1] = hahaFrame.at<uchar>(newY, newX * 3 + 1);
img_p[3 * x + 2] = hahaFrame.at<uchar>(newY, newX * 3 + 2);
}
}
}
}
4、凹透鏡演算法
void compressGlass(Mat hahaFrame,Mat img) {
//【2】凹透鏡
int width = hahaFrame.cols;
int heigh = hahaFrame.rows;
Point center(width / 2, heigh / 2);
for (int y = 0; y<heigh; y++)
{
uchar *img_p = img.ptr<uchar>(y);
for (int x = 0; x<width; x++)
{
double theta = atan2((double)(y - center.y), (double)(x - center.x));
int R = sqrtf(norm(Point(x, y) - center)) * 8; //直接關係到擠壓的力度,與R成反比;
int newX = center.x + (int)(R*cos(theta));
int newY = center.y + (int)(R*sin(theta));
if (newX<0)
newX = 0;
else if (newX >= width)
newX = width - 1;
if (newY<0)
newY = 0;
else if
(newY >= heigh) newY = heigh - 1;
img_p[3 * x] = hahaFrame.at<uchar>(newY, newX * 3);
img_p[3 * x + 1] = hahaFrame.at<uchar>(newY, newX * 3 + 1);
img_p[3 * x + 2] = hahaFrame.at<uchar>(newY, newX * 3 + 2);
}
}
}
相關文章
- 哈哈哈哈哈哈哈哈哈
- 學習OpenCV:濾鏡系列(6)——風OpenCV
- 學習OpenCV:濾鏡系列(8)——素描OpenCV
- openCV實戰專案--人臉考勤OpenCV
- 我給你講個TCP的笑話吧,哈哈哈哈哈哈TCP
- 學習OpenCV:濾鏡系列(1)—— 雕刻&浮雕OpenCV
- 學習OpenCV:濾鏡系列(4)——波浪:正弦OpenCV
- 學習OpenCV:濾鏡系列(7)——漩渦OpenCV
- 【opencv實戰】影象素描及卡通化OpenCV
- web前端面試總結(自認為還算全面哈哈哈哈哈!!!!)Web前端面試
- Opencv專案實戰:14 手勢控制音量OpenCV
- Day3 紀念下第一行程式碼哈哈哈哈哈行程
- 程式設計師和產品經理大家,公司竟然給出了這樣的懲罰。網友:哈哈哈哈哈哈哈哈,大快人心程式設計師
- 學習OpenCV:濾鏡系列(2)——擴張&擠壓OpenCV
- 學習OpenCV:濾鏡系列(9)——擴散(毛玻璃)OpenCV
- 學習OpenCV:濾鏡系列(14)——載入選區OpenCV
- 學習OpenCV:濾鏡系列(15)——羽化(模糊邊緣)OpenCV
- 面試官問我JVM調優,我忍不住了,哈哈哈哈面試JVM
- OpenCv人臉檢測技術-(實現抖音特效-給人臉戴上墨鏡)OpenCV特效
- 學習OpenCV:濾鏡系列(12)——計算模式(強光)OpenCV模式
- 【實戰】基於OpenCV的水錶字元識別(OCR)OpenCV字元
- opencv實戰——影像矯正演算法深入探討OpenCV演算法
- Spring技術內幕筆記2--我懶不寫了哈哈哈哈。Spring筆記
- OpenCV-Python實戰(16)——人臉追蹤詳解OpenCVPython
- 《OpenCV 4.5計算機視覺開發實戰:基於Python》OpenCV影像處理入門書OpenCV計算機視覺Python
- 【opencv】顯微鏡/投影儀 圓陣列標定板標定OpenCV陣列
- 學習OpenCV:濾鏡系列(5)——徑向模糊:縮放&旋轉OpenCV
- 學習OpenCV:濾鏡系列(13)——計算模式演算法集合OpenCV模式演算法
- Python-OpenCV 處理影象(二):濾鏡和影象運算PythonOpenCV
- 物體檢測實戰:使用 OpenCV 進行 YOLO 物件檢測OpenCVYOLO物件
- 【日記】不好意思,阻止你們光合作用了哈哈哈哈(822 字)
- 學習OpenCV:濾鏡系列(11)——高反差保留 (6.30修改版)OpenCV
- 淘寶放大鏡實現
- C/C++,Qt,Python,OpenCV小專案實戰-實時桌面顏色查詢C++QTPythonOpenCV
- 自己解決了一個問題`` 值得慶祝哈哈哈(INTER-ORG TRANSFER)
- 學習OpenCV:濾鏡系列(10)——懷舊色 & 連環畫 & 熔鑄 & 冰凍OpenCV
- 《OpenCV 4.5計算機視覺開發實戰(基於VC++)》簡介OpenCV計算機視覺C++
- [OpenCV實戰]1 基於深度學習識別人臉性別和年齡OpenCV深度學習