OpenCV轉換色彩空間及邊緣偵測

是的
有好一陣子沒有更新了
原因很多
面試阿,碩士研究,出國阿
最近自己也轉換了未來目標
希望自己能變得越來越厲害

在開始之前
須講一下之前的寫入影像
當時的函數是這樣的
VideoWriter writer("test.avi", CV_FOURCC('D', 'I', 'V', 'X'), 30.0, cvSize(640, 480));
後來因為研究只需要寫入單通道影像
也因為需要記憶體控制不能儲存太多數據
也才發現,其實是可以直接寫入單通道影像的
VideoWriter writer("test.avi", CV_FOURCC('D', 'I', 'V', 'X'), 30.0, cvSize(640, 480), 1);
最後面參數,0代表寫入單通道影像,大於0則是多通道





接下來開始今天的主題拉
在影像"前處理"
通常不一定是使用RGB色彩空間
常見的還有YCbCr,HSI等等
不同的應用,會轉換到不同的色彩空間
像是之前我處理膚色問題時
就會用到YCbCr

轉換色彩空間
就可以使用cvtColor這個函式
其用法如下
cvtColor(src, dst, code);
src為來源圖像
dst為儲存圖像
code為轉換方式
如果彩圖要轉灰階
就是使用CV_BGR2GRAY
在opencv中,圖像的通道是用BGR的方式儲存的!
那其實網路上都有色彩空間中轉換的方式
要自己寫也不成問題哦
其他還有像是
CV_BGR2HSV、CV_BGR2YCrCb、CV_BGR2Lab等等
那記得在轉換時,要搞清楚儲存的位元跟通道數量哦
最後若要顯示,都必須轉成8位元的方式儲存
不然圖片會顯示不出來

另外前處理或許還有個重要的過程
就是找edge
邊緣資訊在影像處理中扮演非常重要的腳色
但也因為邊緣資訊的方法
目前都沒有個定論
所以還是要看個人所需處理的目標所決定
在這舉三個例子
最常用的sobel及canny
還有Laplacian
sobel其實就是梯度的運算
梯度的解釋可在 維基 找到
簡單來說可以找出其像素點跟周圍的變化量
在使用sobel時
會需要對x跟y方向作微分
這時會儲存為16S的格式
做完微分之後
再把x跟y方向加起來以及變正數
此時就會儲存為8U的型態
就可以顯示了
這通常還會搭配到thresh hold使用來尋找所需要的邊緣
Sobel(src, dst_16s, CV_16S, x_order, y_order, scale, delta, BORDER_DEFAULT)
src為來源
dst儲存圖像記得要用16S的儲存
x_order跟y_order則是用來選擇要對哪個方向作微分
x的話
就會是
Sobel(src, dst_16s, CV_16S, 1, 0, scale, delta, BORDER_DEFAULT)
scale則為sobel運算子的kernel大小
delta,BORDER_DEFAULT則使用預設則可

canny對我來說則是比較有效的方法
因為弱小的edge有可能只是紋理而已
canny對此有增加了設定
弱小的edge附近如有強的edge
才會保留下來
而對我來說會比較有用的原因是
我只需要強大的edge點
且場景變化太大,使用sobel會不好控制thresh hold
Canny(src, dst, lowThreshold , lowThreshold*ratio,  kernel_size );
src為來源
dst儲存圖像
lowThreshold是第一閾值
lowThreshold*ratio是第二閥值,通常為3倍的lowThreshold
kernel_size為sobel運算子的kernel大小

Laplace對我來說則比較陌生也比較少用到
有興趣的人自己去看吧
維基~

以下為程式碼
#include <iostream>
#include <opencv2\opencv.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
char FileName[] = "lena.jpg";
Mat Src;
Mat Gray;
Mat sobel;
Mat canny;
Mat laplacian;
Src = imread(FileName, 1);
//轉灰階
cvtColor(Src, Gray, CV_BGR2GRAY);
//sobel
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
Sobel(Gray, grad_x, CV_16S, 1, 0, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);  //轉成CV_8U
Sobel(Gray, grad_y, CV_16S, 0, 1, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
Mat dst1;
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst1);//x,y方向加在一起
threshold(dst1, sobel, 80, 255, THRESH_BINARY | THRESH_OTSU);
//Canny
Canny(Gray, canny, 60, 120, 3);
//Laplacian
Laplacian(Gray, laplacian, CV_16S, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(laplacian, laplacian);
imshow("Src", Src);
imshow("Gray", Gray);
imshow("Sobel", sobel);
imshow("Canny", canny);
imshow("Laplacian", laplacian);
cvWaitKey();
return 0;
}
以及結果
來源
灰階
sobel
canny
laplace



沒有留言:

張貼留言

About

努力在程式的大海
用力的揮動雙手
找出屬於自己的航線

Blog Archive

Traffic