傅立葉轉換(Fourier Transform)

傅立葉轉換是一對一函數,可以是連續函數或者離散數列,正向傅立葉轉換,是把一個複雜的波,拆解成N個sin和cos組成的波,頻率從0倍到N-1倍,逆向傅立葉轉換,是把N個sin和cos組成的波,頻率從0倍到N-1倍,分別乘上強度、加上相位,再疊加成一個複雜的波。基本上任何的函式可以被無窮的sin和cos函式的加權和來表示,在影像處理上,經由傅立葉轉換將影像從空間域轉成頻率域,經過一些處理,再由反傅立葉轉換從頻率域轉回空間域。

傅立葉轉換時間複雜度為O(N2),實務上通常使用快速傅立葉轉換(Fast Fourier Transform, FFT),將公式的偶數項與奇數項分開整理,把時間複雜度降至O(NlogN),因為必須剛好對半分,所以影像的長、寬都須為2的次方,當長或寬不是2的次方,可在輸入像素末端補零,使得長和寬皆為2的次方。


Fourier Transform

以下示範將影像進行傅立葉轉換,得到頻譜,再從頻譜進行逆向傅立葉轉換,得到原始圖:

int main(){
    Mat inputImg = imread("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
    Mat padded;                         
    int m = getOptimalDFTSize(inputImg.rows);  //m為大於等於inputImg.rows裡的最小值,且須為2、3、5的次方相乘
    int n = getOptimalDFTSize(inputImg.cols); 
    copyMakeBorder(inputImg, padded, 0, m-inputImg.rows, 0, n-inputImg.cols, BORDER_CONSTANT, Scalar::all(0)); //為了效率,所以對影像邊界拓展

    Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
    Mat complexImg;
    merge(planes, 2, complexImg);        
    dft(complexImg, complexImg);            

    split(complexImg, planes);                  //分離通道,planes[0]為實數部分,planes[1]為虛數部分 
    magnitude(planes[0], planes[1], planes[0]); //planes[0] = sqrt((planes[0])^2 + (planes[1])^2
    Mat magI = planes[0];
    magI += Scalar::all(1);                     //magI = log(1+planes[0])
    log(magI, magI);

    magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));  //令邊長為偶數

    //將區塊重排,讓原點在影像的中央
    int cx = magI.cols/2;
    int cy = magI.rows/2;

    Mat q0(magI, Rect(0, 0, cx, cy));   
    Mat q1(magI, Rect(cx, 0, cx, cy));  
    Mat q2(magI, Rect(0, cy, cx, cy));  
    Mat q3(magI, Rect(cx, cy, cx, cy)); 

    Mat tmp;                          
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    q1.copyTo(tmp);                    
    q2.copyTo(q1);
    tmp.copyTo(q2);

    normalize(magI, magI, 0, 1, CV_MINMAX); 

    imshow("輸入圖", inputImg);    
    imshow("頻譜", magI);

    //逆向傅立葉轉換
    Mat ifft;  
    idft(complexImg,ifft,DFT_REAL_OUTPUT);  
    normalize(ifft,ifft,0,1,CV_MINMAX);  
    imshow("逆向求輸入圖",ifft);  
    waitKey();

    return 0;    
}

Fourier Transform

Fourier Transform

Fourier Transform

繼續閱讀 傅立葉轉換(Fourier Transform)

色彩空間轉換(cvtColor)

OpenCV的cvtColor()讓影像在不同色彩空間之中轉換,由於OpenCV從外界讀入圖檔時,比如使用imread()讀取JPEG檔時,Mat內存為BGR而不是RGB格式,所以輸入參數通常使用CV_BGR2XXX、CV_XXX2BGR,代表從BGR色彩空間轉到其他色彩空間,或從其他色彩空間轉到BGR色彩空間,依此類推,CV_RGB2XXX代表從RGB色彩空間轉到其他色彩空間,通常較少使用。

OpenCV 色彩空間轉換

cvtColor(const Mat& src, Mat& dst, int code)

  • src:來源圖,支援 CV_8U、CV_16U、或 CV_32F位元深度,1、3、4通道的圖,有些色彩空間轉換無法在16位元上操作,使用前可參考文件。
  • dst:目標圖,尺寸大小、深度會和來源圖相同。
  • code:指定在何種色彩空間轉換,比如CV_BGR2GRAY、CV_GRAY2BGR、CV_BGR2HSV等。

BGR & Gray

參數:CV_BGR2GRAY、CV_RGB2GRAY、CV_GRAY2BGR、CV_GRAY2RGB

BGR to Gray: Y=0.299R + 0.587G + 0.114*B

Gray to BGR: B=Y,G=Y,R=Y

人眼對綠色的敏感感較大,而對藍色最小,因此綠色權重較大,藍色較小,上述公式為彩色轉灰階的標準。


BGR & YCrCb

參數:CV_BGR2YCrCb、CV_RGB2YCrCb、CV_YCrCb2BGR、CV_YCrCb2RGB

BGR to YCrCb: Y=0.299R + 0.587G + 0.114B,Cr=(R-Y)0.713 + delta,Cb=(B-Y)*0.564 + delta

YCrCb to BGR: R=Y + 1.403(Cr-delta),G=Y – 0.714(Cr-delta) – 0.344(Cb-delta),B=Y + 1.773(Cb-delta)

delta:8位元->128,16位元->32768,浮點數->0.5

其中 Y 是亮度(Luminance),Cb、Cr 是色差(chrominance),Cb是藍色色差 ,Cr是紅色色差,雖然RGB與YCbCr都為三個通道無法節省儲存空間,但實際上可利用人類視覺對亮度比較敏感,而對彩度比較不敏感的特質來減少內存,也就是減少Cb、Cr的取樣個數。取樣格式有4:2:0、4:2:2、4:4:4三種,4:2:0格式代表每2×2的4個像素中,Y 會對每個像素取樣,而色差 CbCr 僅會在第一行兩個像素的中間取樣,因此8位元情況下,原本每個像素需要3byte儲存空間,4個像素需要12個byte,現在只要6個byte,減少了一半的內存,現今像Jpeg、Mpeg4等影像格式,都是利用YCbCr去壓縮的。


BGR & HSV

HSV(hue、saturation、value)用來表示色相、飽和度和明度,這種系統比RGB更接近人對色彩的感知,色相決定顏色的本質,像我們說紅、澄、黃就是指一種色相,飽和度是指顏色的深淺比例,顏色越深飽和度越高,白色所占比例越高,飽和度越低,明度表示顏色的明暗程度,數值越大越亮。

參數:CV_BGR2HSV、CV_RGB2HSV、CV_HSV2BGR、CV_HSV2RGB

BGR to HSV:當我們計算8位元或16位元圖時,R、G、B都轉成0到1之間的浮點數,比如R=R/(R+G+B),接著用下列公式進行轉換,分別得到H、S、V的值。

HSV

如果H<0,則H=H+360,讓H範圍0到360,S範圍0到1,V範圍0到1,接著根據圖的形態,轉換到合適的範圍。8位元:H=H/2,S=S255,V=V255。16位元:H=H,S=S65535,V=V65535。浮點數圖:維持不變。


BGR & HSL

HSL(hue、saturation、lightness)用來表示色相、飽和度和亮度,這種系統比RGB更接近人對色彩的感知,色相決定顏色的本質,像我們說紅、澄、黃就是指一種色相,飽和度是指顏色的深淺比例,顏色越深飽和度越高,白色所占比例越高,飽和度越低,亮度表示顏色的明暗程度,數值越大越亮。

參數:CV_BGR2HLS、CV_RGB2HLS, CV_HLS2BGR, CV_HLS2RGB

BGR to HSV:當我們計算8位元或16位元圖時,R、G、B都轉成0到1之間的浮點數,比如R=R/(R+G+B),接著用下列公式進行轉換,分別得到H、S、L的值。

HSL

如果H<0,則H=H+360,讓H範圍0到360,S範圍0到1,L範圍0到1,接著根據圖的形態,轉換到合適的範圍。8位元:H=H/2,S=S255,L=L255。16位元:H=H,S=S65535,L=L65535。浮點數圖:維持不變


BGR & CIE XYZ

在CIE XYZ色彩空間中,有一組X、Y和Z的值,對應於紅色、綠色和藍色,並不是真的紅藍綠,而是使用匹配函數來計算出來。

參數:CV_BGR2XYZ、CV_RGB2XYZ, CV_XYZ2BGR, CV_XYZ2RGB

CIE XYZ


BGR & CIE Lab*

參數:CV_BGR2Lab、CV_RGB2Lab、CV_Lab2BGR、CV_Lab2RGB

Lab色彩空間是顏色-對立空間,帶有維度L表示亮度,a和b表示顏色對立維度,基於非線性壓縮的CIE XYZ色彩空間坐標,Lab顏色被設計來接近人類視覺,致力於感知均勻性,L分量密切匹配人類亮度感知,因此可以被用來通過修改a和b分量的輸出色階來做精確的顏色平衡,或使用L分量來調整亮度對比。當我們計算8位元或16位元圖時,R、G、B都轉成0到1之間的浮點數,比如R=R/(R+G+B),接著用下列公式進行轉換,分別得到L、a、b的值。

CIE L*a*b*

最後L範圍0到100,a範圍-127到127,b範圍-127到127,接著根據圖的形態,轉換到合適的範圍。8位元:L=L*255/100,a=a+128,b=b+128。16位元:目前不支援。浮點數圖:維持不變。


BGR & CIE Luv*

參數:CV_BGR2Luv, CV_RGB2Luv, CV_Luv2BGR, CV_Luv2RGB

當我們計算8位元或16位元圖時,R、G、B都轉成0到1之間的浮點數,比如R=R/(R+G+B),接著用下列公式進行轉換,分別得到L、a、b的值。

最後L範圍0到100,u範圍-134到220,v範圍-140到122,接著根據圖的形態,轉換到合適的範圍。8位元:L=L255/100,u=255/354(u+134),v=255/262*(v+140)。16位元:目前不支援。浮點數圖:維持不變。

enter image description here

繼續閱讀 色彩空間轉換(cvtColor)

繪圖(line、rectangle、circle、ellipse、polylines、putText)

OpenCV有函式方便我們繪圖,這邊依序介紹如何繪製線、矩形、圓、橢圓、多角形、文字在影像上,其他圖形請參考OpenCV文件。


連通類型

連通類型有兩種,不同的連通型態會影響圖案的邊界部分:

  • 4-連通(4-connectivity):座標(x,y)處的像素 P,其上下左右各有一個鄰近點,其座標分別為:(x+1, y)、(x-1,y)、(x, y+1)、(x, y-1),這樣一組像素稱為P的4近鄰,用 N4(P)表示。
  • 8-連通(8-connectivity):如果除了上式N4(P)的點,加上四個對角鄰近點,(x+1,y+1)、(x+1,y-1)、(x-1,y+1)、(x-1,y-1))加在一起,則稱這八個點為P的8近鄰,用N8(P)表示。

OpenCV 畫線

void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)

  • img:輸入圖,線會畫在上面。
  • pt1:線的起點。
  • pt2:線的終點。
  • color:線的顏色。
  • thickness:線的厚度。
  • lineType:通道型態,可輸入8、4、CV_AA: 8->8通道連結。 4->4通道連結。 CV_AA->消除鋸齒(antialiased line),消除顯示器畫面線邊緣的凹凸鋸齒。

OpenCV 畫矩形

void rectangle(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)

  • img:輸入圖,矩形會畫在上面。
  • pt1:矩形頂點。
  • pt2:矩形頂點,pt1的對角邊
  • color:矩形的顏色。
  • thickness:矩形的邊線寬度,輸入負值或CV_FILLED代表填滿矩形。
  • lineType:通道型態,可輸入8、4、CV_AA: 8->8通道連結。 4->4通道連結。 CV_AA->消除鋸齒(antialiased line),消除顯示器畫面線邊緣的凹凸鋸齒。

OpenCV 畫圓

void circle(Mat& img, Point center, int radius, const Scalar& color, int thickness=1, int lineType=8, int shift=0)

  • img:輸入圖,圓會畫在上面。
  • center:圓心。
  • radius:圓半徑。
  • color:圓形的顏色。
  • thickness:圓形的邊線寬度,輸入負值或CV_FILLED代表填滿圓形。
  • lineType:通道型態,可輸入8、4、CV_AA: 8->8通道連結。 4->4通道連結。 CV_AA->消除鋸齒(antialiased line),消除顯示器畫面線邊緣的凹凸鋸齒。

OpenCV 畫橢圓

void ellipse(Mat& img, Point center, Size axes, double angle, double startAngle, double endAngle, const Scalar& color, int thickness=1, int lineType=8, int shift=0)

  • img:輸入圖,橢圓會畫在上面。
  • center:圓心。
  • axes:橢圓軸的尺寸。
  • angle:旋轉角度,單位角度。
  • startAngle:橢圓弧度起始角度,單位角度。
  • endAngle:橢圓弧度結束角度,單位角度。
  • color:橢圓的顏色。
  • thickness:橢圓的邊線寬度,輸入負值或CV_FILLED代表填滿橢圓形 。
  • lineType:通道型態,可輸入8、4、CV_AA: 8->8通道連結。 4->4通道連結。 CV_AA->消除鋸齒(antialiased line),消除顯示器畫面線邊緣的凹凸鋸齒。

OpenCV 畫多角形

void polylines(Mat& img, const Point** pts, const int* npts, int ncontours, bool isClosed, const Scalar& color, int thickness=1, intlineType=8, int shift=0)

  • img:輸入圖,多角形會畫在上面。
  • pts:包含多角形各個曲線點的陣列。
  • npts:包含多角形各曲線頂點數目的陣列。
  • ncontours:曲線數。
  • isClosed:是否為封閉的多角形。
  • color:多角形的顏色。
  • thickness:多角形的邊線寬度,輸入負值或CV_FILLED代表填滿多角形。
  • lineType:通道型態,可輸入8、4、CV_AA: 8->8通道連結。 4->4通道連結。 CV_AA->消除鋸齒(antialiased line),消除顯示器畫面線邊緣的凹凸鋸齒。

OpenCV 畫文字字串

void putText(Mat& img, const string& text, Point org, int fontFace, double fontScale, Scalar color, int thickness=1, int lineType=8, bool bottomLeftOrigin=false)

  • img:輸入圖,字串會畫在上面。
  • text:輸出字串,OpenCV目前沒有支援中文文字顯現。
  • org:文字左下角位置。
  • fontFace:字體樣式。
  • fontScale:字體大小。
  • color:字串顏色。
  • thickness:構成字串的線寬度。
  • lineType:通道型態,有以下三種可選: 8:8通道連結。 4:4通道連結。 CV_AA:消除鋸齒(antialiased line),消除顯示器畫面橢圓邊緣的凹凸鋸齒。

程式碼

以下程式碼實際繪製線、矩形、圓、橢圓、多角形、文字在影像上:

#include <cstdio>
#include <opencv2/opencv.hpp>
using namespace cv;

int main(){
    Mat img(400, 400, CV_8UC3, Scalar(255,255,255));
    line(img, Point(20,40), Point(120,140), Scalar(255,0,0), 3);
    rectangle(img, Point(150,40), Point(250,140), Scalar(0,0,255), -1);
    circle(img, Point(330,90), 50, Scalar(0,255,0), -1);
    ellipse(img, Point(80,280), Size(60,40), 45, 0, 360, Scalar(255,255,0), 2);

    Point points[1][5];
    points[0][0] = Point(150, 270);
    points[0][1] = Point(190, 220);
    points[0][2] = Point(260, 255);
    points[0][3] = Point(224, 296);
    points[0][4] = Point(178, 316);
    const Point* ppt[1] = {points[0]};
    int npt[] = {5};
    polylines(img, ppt, npt, 1, 1, Scalar(0,255,255),5);

    putText(img, string("OpenCV"), Point(280,280), 0, 1, Scalar(0,0,0),3);

    imshow("window", img);
    waitKey(0); 

    return 0;
}

OpenCV 繪圖

繼續閱讀 繪圖(line、rectangle、circle、ellipse、polylines、putText)