2008年6月30日 星期一

OpenCV繪圖的實作-cvFillPoly

接下來的也是實心多邊型函數,但是這個功能強大多了,也有點複雜,它可以藉由點集合陣列的分群,來做出兩個以上的多邊型,但是對點集合有幾個限制的條件.

cvFillPoly()函式的實作
#include <cv.h>
#include <highgui.h>

IplImage *Image1;
CvPoint PointArray1[7];
CvScalar Color;

CvSize TextSize;
CvFont Font1;
CvPoint TextPosition;
int main()
{
    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);

    PointArray1[0]=cvPoint(200,200);
    PointArray1[1]=cvPoint(400,150);
    PointArray1[2]=cvPoint(650,230);
    PointArray1[3]=cvPoint(800,300);
    PointArray1[4]=cvPoint(900,550);
    PointArray1[5]=cvPoint(700,600);
    PointArray1[6]=cvPoint(100,500);

    CvPoint *PointArray[2]= {&PointArray1[0],&PointArray1[4]};

    CvScalar Color=CV_RGB(255,0,0);
    int PolyVertexNumber[2]={4,3};
    int BlockNumber=2;

    cvFillPoly(Image1,PointArray,PolyVertexNumber,BlockNumber,Color,CV_AA,0);

    cvInitFont( &Font1,CV_FONT_HERSHEY_SIMPLEX,0.5,0.5,0.0,1, CV_AA );

    cvPutText(Image1,"1(200,200)",cvPoint(180,180),&Font1,CV_RGB(255,0,0));
    cvPutText(Image1,"2(400,150)",cvPoint(400,150),&Font1,CV_RGB(255,0,0));
    cvPutText(Image1,"3(650,230)",cvPoint(650,230),&Font1,CV_RGB(255,0,0));
    cvPutText(Image1,"4(800,300)",cvPoint(800,300),&Font1,CV_RGB(255,0,0));
    cvPutText(Image1,"5(900,550)",cvPoint(900,550),&Font1,CV_RGB(255,0,0));
    cvPutText(Image1,"6(700,600)",cvPoint(700,620),&Font1,CV_RGB(255,0,0));
    cvPutText(Image1,"7(100,500)",cvPoint(90,520),&Font1,CV_RGB(255,0,0));

    cvNamedWindow("FillPoly",1);
    cvShowImage("FillPoly",Image1);
    cvWaitKey(0);
}

執行結果:
(1)原始結果


(2)BlockNumber=2,PolyVertexNumber[2]={3,3}


(3)BlockNumber=1,PolyVertexNumber[1]={3,3}


(4)BlockNumber=1,PolyVertexNumber[1]={7}


這裡給它了7個點的點集合,並且用CvFont跟cvPutText()的方式列出他們點座標的位置,跟之前cvFillConvexPoly()不一樣的地方,它把點集合放在二維陣列裡,這裡的二維陣列,可以自己設定每個空間長度都不一樣,像上面的程式範例,這個二維陣列被分為兩個,第一個長度為4第二個長度為3,然後設定要繪製兩個多邊型區塊,選定第一個長度為四的要用到4個點,第二個長度為三的要用到3個點,接著就繪製實心多邊型,這樣的目的,實際上可以轉換成另一個問題來解釋:

有七個點集合,它們被分為兩群,第一群有四個點集合,第二群被分為三個點集合,要從這兩群裡面各做一個多邊型,要選定要製作多邊型的點,第一群不能超過四個點,第二群不能超過三個點,而所需要製作的多邊形區塊數為兩個,因此這裡選擇了製作出一個四邊形跟一個三邊型,而四邊形跟三角形的點座標就是被分開的那兩群.

因此,這裡的輸入就出現了限制條件
1.七個點集合最多只能被分成1~7群
2.挑選點數目的群數時可以小於等於一開始所設定的分群數,當小於時,後面的的點數量會累加,
  每群被選定的點數目不能超過每一群裡面的所有點數量
3.多邊型的區塊數必須小於等於所有的群數

接著是延伸比較難的部份,一個點集合有N個,可以將點集合任意的分群,而每一群只能製作一個多邊型,接著,選定要製作多少個多邊型區塊,再從每一群中挑出自己選定的點個數來製作多邊型.

因此,可以分析的出來點的分群可以分成
1.1≦Group≦N這麼多群
2.每一群可以選定的點為1≦PolyVertexNumber(i)≦MAX_POINT_NUMBER(i),PolyVertexNumber可以設定的群
   數為,1≦n≦Group
     當n<Group,則
   MAX_POINT_NUMBER(n)=MAX_POINT_NUMBER(n+1)+MAX_POINT_NUMBER(n+2)+...MAX_POINT_NUMBER(Group)
3.可以製作的多邊型數目為1≦PolyBlockNumber≦n

為什麼要有這樣的限制條件,因為如果沒照這限制條件的話cvFillPoly()就會出現錯誤,當然啦,它可以將點集合分群,決定繪製多邊型數,及選定被分群的點集合數目,超出範圍當然畫不出來啦.

七個點集合被分群的情形


點集合資料結構的排列狀態


也可以用三個陣列實作多邊型二維陣列


如果用三個以上的陣列實作的話,將會影響記憶體區段,原本存取的是連續記憶體區段,卻會變成不連續的記憶體,會影響到空間局部性,也就是存取記憶體時的分頁替換會造成速度下降,這是作業系統原理的分頁相關部分.

cvFillPoly()
製作多個或一個填充多邊型,第一個引數放目標圖片,下一個一定要放CvPoint資料結構二維陣列,再來是二維陣列內多邊型點集合數分配,各二維陣列內陣列點集合區塊數,顏色,線條種類,比例大小縮放.
cvFillPoly(IplImage資料結構,CvPoint二維陣列,分配二維陣列的各多邊型頂點,選定需要製作幾個多邊型,CvScalar顏色,線條種類,比例大小縮放數據)



OpenCV繪圖的實作-cvFillConvexPoly

這是個繪製實心多邊型的函式,OpenCV的說明上聲稱比cvFillPoly()同樣是繪製實心多邊型的函式還快,而且,這個函式的製作比cvFillPoly()還簡單.要繪製它時,當然,需要多個點,多邊型,需要三個以上的頂點,然後依著頂點順序來做多邊型的繪製,並且選定想繪製幾個頂點的多邊型,選定顏色,種類等其他的.

cvFillConvexPoly()函式的實作
#include <cv.h>
#include <highgui.h>


IplImage *Image1;

CvPoint PointArray1[6];
CvScalar Color;
int PolyVertexNumber;
int Shift;
int main()
{
    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1, IPL_DEPTH_8U, 3);

    PointArray1[0]=cvPoint(200,200);
    PointArray1[1]=cvPoint(400,150);
    PointArray1[2]=cvPoint(650,230);
    PointArray1[3]=cvPoint(800,300);
    PointArray1[4]=cvPoint(900,550);
    PointArray1[5]=cvPoint(100,500);

    CvScalar Color=CV_RGB(255,0,0);
    PolyVertexNumber=6;
    Shift=0;

    cvFillConvexPoly(Image1,PointArray1,PolyVertexNumber,Color,CV_AA,Shift);

    cvNamedWindow("FillConvexPoly",0);
    cvShowImage("FillConvexPoly",Image1);
    cvWaitKey(0);
}

執行結果:
(1)PolyVertexNumber=3

(2)PolyVertexNumber=4

(2)PolyVertexNumber=5

(2)PolyVertexNumber=6


這個繪圖函式真的比cvFillPoly()簡單的多了!內部使用的演算法也不一樣,總而言之,給定點座標集合,多邊型頂點數,顏色,線條種類,比例縮放,就可以繪出多邊型了,而執行結果,表示的是不同多邊型頂點數的執行結果.由圖可知,它的演算法是照著點陣列的排列的順序來的.

cvFillConvexPoly()
利用點陣列集合繪製多邊型,並且給它多邊型的頂點數,它會依照陣列的順序去製作多邊型圖案如果順序給的繪不出多邊型,則會亂掉.
cvFillConvexPoly(IplImage資料結構,CvPoint陣列,多邊型頂點數,CvScalar顏色,線條類型,比例縮放數據)



OpenCV繪圖的實作-cvCircle,cvEllipse,cvEllipseBox

圓的製作,要給他目標圖片,圓心座標,半徑,其他參數等.

cvCircle()函式的實作
#include <cv.h>
#include <highgui.h>

IplImage *Image1;

CvPoint CircleCenter;
int Radius;
CvScalar Color;
int Thickness;
int Shift;

int main()
{
    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);

    CircleCenter=cvPoint(500,350);
    Radius=300;
    Color=CV_RGB(255,0,0);
    Thickness=3;

    cvCircle(Image1,CircleCenter,Radius,Color,Thickness,CV_AA,Shift);

    cvNamedWindow("Circle",1);
    cvShowImage("Circle",Image1);
    cvWaitKey(0);
}

執行結果:


執行出來的結果會是一個座標(500,300),半徑300的紅色圓形.

cvCircle()
繪出圓形的函式,需要給它目標圖形IplImage資料結構,CvPoint圓心座標,半徑長度,CvScalar顏色,粗細,線條種類,縮放比例數據.
cvCircle(IplImage資料結構,CvPoint圓心座標,半徑數據,CvScalar顏色,粗細數據,線條種類,縮放比例數據)




橢圓形,用的是cvEllipse()的函式,這個繪圖也很特殊,可以用橢圓函式畫弧線,需要用到的是,目標圖形IplImage資料結構,橢圓圓心,橢圓長短軸,橢圓旋轉角度,繪製起點(角度),繪製終點(角度)及其他參數.

cvEllipse()函式的實作
#include <cv.h>
#include <highgui.h>

IplImage *Image1;
CvPoint CircleCenter;
CvSize EllipseAxes;
double RotateAngle;
double StartDrawingAngle;
double StopDrawingAngle;
CvScalar Color;
int Thickness;
int Shift;

int main()
{
    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);

    CircleCenter=cvPoint(500,400);
    EllipseAxes=cvSize(300,200);
    RotateAngle=0;
    StartDrawingAngle=0;
    StopDrawingAngle=360;
    Color=CV_RGB(255,0,0);
    Thickness=2;
    Shift=0;

    cvEllipse(Image1,CircleCenter,EllipseAxes,RotateAngle,
                 StartDrawingAngle,StopDrawingAngle,Color,Thickness,CV_AA,Shift);

    cvNamedWindow("Ellipse",1);
    cvShowImage("Ellipse",Image1);
    cvWaitKey(0);
}

執行結果:
(1)RotateAngle=0,StartDrawingAngle=0,StopDrawingAngle=360


(2)修改為RotateAngle=70,StartDrawingAngle=100,StopDrawingAngle=360


上面的執行結果,第一張是標準的橢圓,給它圓心,長短軸半徑,旋轉角度,繪圖起點,繪圖終點,顏色,粗細,線條種類,縮放比例,而弧線的部份,修改了旋轉角度,繪圖起點跟繪圖終點,如果繪圖起點是0~360那就一定是橢圓了,只要他的角度不會繞一圈,愛畫怎樣的弧線都行.

cvEllipse()
可以繪橢圓跟弧線,需要用到目標圖形IplImage資料結構,CvSize長短軸半徑,旋轉角度數據,繪圖起點,繪圖終點的角度數據,CvScalar顏色,粗細數據,線條種類,縮放比例.
cvEllipse(IplImage資料結構,CvSize長短軸,旋轉角度數據,繪圖起點角度,繪圖終點角度,CvScalar顏色,粗細數據,線條種類,縮放比例數據)




下面的這個跟cvEllipse()很像,但是他是屬於OpenCV演算法專用的繪圖函數,可以藉由一個方框來繪製橢圓,利用ROI(Region Of Interest)的方式,OpenCV的應用上有cvCamShift,cvMinAreaRect,cvFitEllipse等.

cvEllipseBox()函式的實作
#include <cv.h>
#include <highgui.h>


IplImage *Image1;
CvBox2D Box1;
CvScalar Color;
int Thickness;
int Shift;

int main()
{
    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);

    Box1.center=cvPoint2D32f(500,400);
    Box1.size=cvSize2D32f(300,200);
    Box1.angle=70;
    Color=CV_RGB(255,0,0);
    Thickness=2;
    Shift=0;

    cvEllipseBox(Image1,Box1,Color,Thickness,CV_AA,Shift);

    cvNamedWindow("EllipseBox",1);
    cvShowImage("EllipseBox",Image1);
    cvWaitKey(0);
}

執行結果:


這邊可輸入的參數少了,也只能單純的話橢圓,然後讓他旋轉,其他功能都與cvEllipse()類似,而圓心,長短軸長度,旋轉角度,都以CvBox資料結構來代替,OpenCV的演算法也拿CvBox的方框來實作出橢圓.

cvEllipseBox()
給定目標IplImage資料結構,在給它方框CvBox資料結構,藉由方框資料結構的參數繪出橢圓,在給定線條粗細,線條種類,縮放比例大小等數據
cvEllipseBox(IplImage資料結構,CvBox方框資料結構,CvScalar顏色,線條粗細數據,線條種類,縮放比例大小數據)



2008年6月29日 星期日

OpenCV繪圖的實作-cvLine,cvRectangle

接下來是一系列的繪圖函式的實作,它跟CvFont一樣被包在"cxcore.h"裡,而"cxcore.h"則是在"cv.h"裡就有包含到了,cvLine()顧名思義,就是畫直線,由一個起始點及一個結束點,及一些附屬參數,以下程式碼

cvLine()函式的實作
#include <cv.h>
#include <highgui.h>


IplImage *Image1,*Image2;
CvPoint FromPoint1,ToPoint1;
CvPoint FromPoint2,ToPoint2;
CvScalar Color;
int Thickness;
int Shift;

int main()
{
    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);
    Image2=cvCloneImage(Image1);

    FromPoint1 = cvPoint(100,100);
    ToPoint1 = cvPoint(800,600);

    FromPoint2=cvPoint(800,100);
    ToPoint2 = cvPoint(100,600);

    Color=CV_RGB(255,0,0);
    Thickness=3;

    Shift=1;
    cvLine(Image1,FromPoint1,ToPoint1,Color,Thickness,CV_AA,Shift);
    cvLine(Image1,FromPoint2,ToPoint2,Color,Thickness,CV_AA,Shift);

    cvNamedWindow("cvLine shift",1);
    cvShowImage("cvLine shift",Image1);

    Shift=0;
    cvLine(Image2,FromPoint1,ToPoint1,Color,Thickness,CV_AA,Shift);
    cvLine(Image2,FromPoint2,ToPoint2,Color,Thickness,CV_AA,Shift);

    cvNamedWindow("cvLine",1);
    cvShowImage("cvLine",Image2);
    cvWaitKey(0);
}

執行結果:
(1)Shift=1

(2)Shift=0

(3)將Shift=0,Shift=1繪在同一張圖上



上面的圖,畫出來的是一個X,除了設頂起始點,結束點,還有線條顏色,粗細,線條種類及shift,這邊的shift很特殊,它會將直線線段等比例縮小1倍,也就是"cvLine shift"的直線線段座標將會是(50,50),(400,300)跟另一條直線為(400,50),(50,300),如果shift給2的話將會是(25,25),(200,150)跟(200,25),(25,150)也就是(x軸座標/2^(shift),y軸座標/2^(shift))的縮放比例,shift為非負整數.

cvLine()
繪製直線的函式,必須給它目標圖片資料結構(IplImage)起點點座標,終點點座標,直線顏色,線條粗細,線條種類,及縮放比例大小,縮放比例大小(Shift)為0的話就是原始線段長度
cvLine(IplImage資料結構,起點CvPoint資料結構,終點CvPoint資料結構,色彩CvScalar資料結構,線條粗細數據,線條種類數據,縮放比例數據)




cvRectangle(),為方形的繪圖函式,給定他對角線的值,頂點1及頂點3,再來就是其他的參數設定.

cvRectangle()方形的實作
#include <cv.h>
#include <highgui.h>


IplImage *Image1;
CvPoint VertexOne,VertexThree;
CvScalar Color;
int Thickness;
int Shift;

int main()
{
    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);

    VertexOne=cvPoint(200,200);
    VertexThree=cvPoint(800,600);

    Color=CV_RGB(255,0,0);
    Thickness=2;
    Shift=0;

    cvRectangle(Image1,VertexOne,VertexThree,Color,Thickness,CV_AA,Shift);

    cvNamedWindow("Rectangle",1);
    cvShowImage("Rectangle",Image1);
    cvWaitKey(0);

    free(&VertexOne);
    free(&VertexThree);
    free(&Color);
    cvReleaseImage(&Image1);
    cvDestroyWindow("Rectangle");
}

執行結果:



這個程式繪出了600*400的長方形,給定了對角線的兩個點,顏色,粗細,線條種類及是否等比例縮放.

cvRectangle()
繪出長方形,給它目標IplImage資料結構,頂點1及頂點3的CvPoint資料結構,顏色,粗細,線條種類及是否等比例縮放.
cvRectangle(IplImage資料結構,頂點1CvPoint資料結構,頂點3CvPoint資料結構,CvScalar資料結構顏色,粗細數據,線條種類,等比例縮放數據)



OpenCV字型的實作-CvFont製作

字型的部份,用到的是CvFont資料結構,在"cxcore.h"的函式庫裡,它有許多的參數可以設定,專門的功能是在圖片上顯示文字訊息,也可以應用在視訊播放等功能.

字型及輸出文字程式設計
#include <cv.h>
#include <highgui.h>


IplImage *Image1;
double Scale;
int Thickness;
CvScalar Color;

int FontFace;
double HorizontalScale;
double VerticalScale;
double Shear;
int LineType;
CvPoint TextPosition1,TextPosition2,TextPosition3;

int main()
{

    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);

    Scale=1;
    Thickness=1;

    CvFont Font1=cvFont(Scale,Thickness);

    TextPosition1=cvPoint(200,300);
    Color=CV_RGB(255,0,0);

    cvPutText(Image1,"The Text,in OpenCV,just only wrote in English--",TextPosition1,&Font1,Color);

    FontFace=CV_FONT_HERSHEY_SIMPLEX;
    HorizontalScale=1;
    VerticalScale=1;
    Shear=0.1;
    Thickness=1;
    LineType=CV_AA;
    TextPosition2=cvPoint(200,350);
    TextPosition3=cvPoint(200,400);

    cvInitFont(&Font1,FontFace,HorizontalScale,VerticalScale,Shear,Thickness,LineType);

    cvPutText(Image1,"It did'nt write in Chinese,",TextPosition2,&Font1,Color);
    cvPutText(Image1,"because it did'nt support UTF-8.",TextPosition3,&Font1,Color);

    cvGetTextSize("because it did'nt support UTF-8.",&Font1,&TextSize,&Baseline);
    printf("width=%d\nheight=%d\nBaseline=%d\n",TextSize.width,TextSize.height,Baseline);

    cvNamedWindow("Font Test",1);
    cvShowImage("Font Test",Image1);
    cvWaitKey(0);
}

執行結果:


上面的文字訊息,表達的是OpenCV只支援ASCII code的文字資訊輸入,一開始先給一個IplImage資料結構,給他一個通道3的8bits空間,再用cvFont()對它初始化,Scale表示字型長寬比例設定,1是原始大小,Thickness是線條的粗細,1亦是原始粗細,而字體類型則被OpenCV的cvFont()預設了,接著在cvPutText()放置目標圖形,文字內容,文字座標,文字顏色,文字的顏色則是用Channel為3的RGB三原色所組成的,因此,使用了CV_RGB()的函式,所有顏色的種類都可以被R+G+B的數字組合.再來的是用cvInitFont()將CvFont資料結構重新初始化,在做CvFont的時候,要用cvFont()或是cvInitFont()來做初始化哪個都行,但cvInitFont()可輸入的文字資訊則較多,cvInitFont()則可輸入字體(FontFace),水平比例(HorizontalScale),垂直比例(VerticalScale),頃斜程度(Shear),字體粗細(Thickness),線條種類(LineType).再用cvPutText()輸出文字內容.最後的cvGetTextSize()則是取得文字方框資訊,像是前面的Drawing的OpenCVForever()一樣,方便座標的排版可以取得文字方框的高度及寬度,以及每提升2.4高度就提升1個Baseline.

cvFont()
初始化CvFont資料結構,給他一個Scale代表字型比例Scale代表HorizontalScale跟VerticalScale的數值,而它的字體被預設為CV_FONT_HERSHEY_PLAIN,而它的另一個參數是字體粗細
cvFont(字型大小比例,字體粗細)

cvInitFont()
亦是做CvFont初始化動作,給他一個CvFont資料結構,字型種類,它的字型種類(Font face)被OpenCV定義為

#define CV_FONT_HERSHEY_SIMPLEX 0
#define CV_FONT_HERSHEY_PLAIN 1
#define CV_FONT_HERSHEY_DUPLEX 2
#define CV_FONT_HERSHEY_COMPLEX 3
#define CV_FONT_HERSHEY_TRIPLEX 4
#define CV_FONT_HERSHEY_COMPLEX_SMALL 5
#define CV_FONT_HERSHEY_SCRIPT_SIMPLEX 6
#define CV_FONT_HERSHEY_SCRIPT_COMPLEX 7

而他接著的輸入是水平比例,鉛直比例,頃斜程度,字體粗細,線條種類,它的線條種類被定義為4,8,16,其中16的參數為

#define CV_AA 16

但4跟8卻沒被定義,4表示4鄰接,8表示8鄰接,用的演算法為Bresenham的線條演算法.
cvInitFont(CvFont資料結構,字型種類參數或代號,水平比例數據,鉛直比例數據,頃斜程度數據,字體粗細數據,線條種類參數或代號)

cvPutText()
為文字輸入的地方,給他IplImage資料結構,文字,文字座標,CvFont資料結構,文字顏色.
cvPutText(IplImage資料結構,"文字字串",CvPoint資料結構文字座標,CvFont資料結構,CvScalar資料結構文字顏色)

cvGetTextSize()
輸入一段文字及給它CvFont資料結構,他可以幫忙預估文字方框的長寬及Baseline,而必須要先給它空的CvSize資料結構的記憶體位址及空的int記憶體位址.
cvGetTextSize("文字字串",CvFont資料結構,空的CvSize資料結構,空的int型別變數);



2008年6月28日 星期六

OpenCV隨機的實作-CvRandState的實作

OpenCV也提供了另一個資料結構來做Rand的實作,CvRandState,被包在"cvcompat.h"裡,"cvcompat.h"的完整意思是Computer Vision Compatility,CvRandState的特色是可以選定常態分佈及均勻分佈,不過,它只能運用在矩陣跟一維陣列.

矩陣的部份程式碼如下

RandState矩陣的實作
#include <cv.h>
#include <highgui.h>


int main()
{
    IplImage *Image1;

    CvSize ImageSize1 = cvSize(320,240);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);
    cvZero(Image1);

    CvRandState RandState1;
    cvRandInit(&RandState1,0,255,cvGetTickCount(),CV_RAND_UNI);

    cvRandSetRange(&RandState1,0,127,0);
    cvRandSetRange(&RandState1,128,255,1);

    cvRand(&RandState1,Image1);

    cvNamedWindow("cvRand",1);
    cvShowImage("cvRand",Image1);
    cvWaitKey(0);
}

執行結果:


這個結果也好玩了,因為它隨機的範圍RGB值都不一樣,因此執行結果為偏綠色的隨機數,這邊,開啟了一個Channel為3的RGB圖形,給他全0的值,讓他變成黑色,接著,使用CvRandState資料結構,用cvRandInit()初始化它,隨機範圍是0~255,種子是時間(可輸入大小為64bits),均勻分佈,這裡會使個它的隨機RGB值範圍都是0~255,接著,用cvRandSetRange()改變了B(第三個參數)值的範圍為0~127,再用cvRandSetRange()改變G值範圍為128~255,而R值,沒有被cvRandSetRange()改變到,因此它的範圍是0~255,再來就是設定圖形啦,給它的是Image1及被設定過後的RandState1,會對圖形自動產生隨機,跟CvArr()一樣,不過這邊可以任意改變RGB值範圍,而這裡不一定是要用到通道3的RGB值,可以用通道格式為1,2,3的圖形.

再來是一維陣列的實作

RandState一維陣列的實作
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>


int main()
{
    float FloatArray[20];
    CvRandState RandState1;
    cvRandInit(&RandState1,30,20,cvGetTickCount(),CV_RAND_NORMAL);
    cvbRand(&RandState1,FloatArray,20);
    for(int i=0;i<20;i++)
    {
        printf("%f\n",FloatArray[i]);
    }
    system("pause");

    free(&RandState1);
}

執行結果:


這邊開啟了一個空間為20個float陣列,RandState1資料結構及初始化它,給他常態分佈,平均數為30標準差為20,及以時間的種子,再用cvbRand()製作常態分佈隨機陣列,給他CvRandState資料結構,float陣列名稱,及隨機的大小,亦可以隨機到前十個陣列空間就好.這裡陣列規定要用Float型別做隨機實作,在做printf()就必須要用"%f".

cvRandInit()
初始化CvRandState資料結構,可以選定隨機分佈的種類,並給定它種子,有兩種情形
cvRandInit(CvRandState資料結構,隨機上界,隨機下界,均勻分佈參數,64bits種子的數字)
cvRandInit(CvRandState資料結構,平均數,標準差,常態分佈參數,64bits種子的數字)

cvRandSetRange()
修改CvRandState資料結構的參數內容,均勻分佈的話可以每個通道的上下界常態分佈可以修改每個通道的平均數,標準差.
cvRandSetRange(CvRandState資料結構,均勻分佈上界,均勻分佈下界,目標通道數據)
cvRandSetRange(CvRandState資料結構,常態分佈平均數,常態分佈標準差,目標通道數據)

cvRand()
將CvMat或IplImage資料結構隨機化,用被設定過的CvRandState資料結構來隨機.
cvRand(CvRandState資料結構,CvMat或IplImage資料結構)

cvbRand()
將一維陣列隨機化,可以設定隨機的長度
cvbRand(RandState資料結構,Float型別陣列名稱,隨機的長度);



OpenCV隨機的實作-圖形,矩陣點座標隨機交換

這個隨機是很特殊的隨機,cvRandShuffle()他會將圖片或矩陣點的位置隨機重排,這邊主要是用在分群演算法的應用,運用隨機交換的方式將矩陣座標的位置隨機重排.

RandShuffle的使用
#include <cv.h>
#include <highgui.h>


int main()
{
    IplImage *Image1,*Image2;
    Image1=cvLoadImage("bicycle.jpg",1);
    Image2=cvCloneImage(Image1);

    CvRNG rng;
    rng = cvRNG(cvGetTickCount());

    double IterFactor=0.1;

    cvRandShuffle(Image2,&rng,IterFactor);

    cvNamedWindow("Bicycle",1);
    cvShowImage("Bicycle",Image1);
    cvNamedWindow("Bicycle Rand Shuffle",1);
    cvShowImage("Bicycle Rand Shuffle",Image2);
    cvWaitKey(0);
}
圖片:


執行結果:
(1)IterFactor=0.1

(把圖片放大,其實有雜訊的)
(2)IterFactor=1

(隨機了640*480次,但仍看的到影像輪廓)

用出來的結果很像胡椒鹽雜訊(Salt-and-peper noise),但實際上不是,cvRandShuffle()的第三個參數放的是交換數,為double型別,可以用小數去代替,這邊用0.1,原圖影像為640*480則它隨機交換的次數是640*480*0.1,IterFactor大於1以上圖片就很難辨識清楚了,因為它幾乎所有點都被交換過了(640*480*1),不過他選定交換的點是隨機的,所以用1的話仍然可以看的見圖形模糊的輪廓.這邊他交換的是R+G+B的像素值(pixel),也就是說他會依照圖片的格式做座標的交換,不用擔心它交換的只是一個單一的數值.

cvRandShuffle()
將CvMat矩陣或IplImage圖片做隨機座標交換,隨機交換的個數由第三個參數來做決定.第三個參數為長*寬*IterFactor的結果.
cvRandShuffle(CvMat或IplImage資料結構,CvRNG資料結構,交換的比率參數)



2008年6月27日 星期五

OpenCV隨機的實作-隨機分佈的種類

在這裡,要用到的是cvRandArr()這個函式,來介紹隨機分佈的種類--常態分佈(Normal Distribution)與均勻分佈(Uniform Distribution),這兩個的差異在於所有數字的分佈機率不同,而常態分佈(分配)可以定義在很多地方,自然界的狀態大多都屬於常態分佈,因此,在圖形上很多都屬於常態分佈的.

隨機分佈程式碼
#include <cv.h>
#include <highgui.h>


int main()
{
    IplImage *Image1,*Image2;
    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,IPL_DEPTH_8U,3);
    cvZero(Image1);
    Image2 = cvCloneImage(Image1);

    CvRNG rng=cvRNG(cvGetTickCount());

    int RandType1=CV_RAND_UNI;
    int RandType2=CV_RAND_NORMAL;

    CvScalar UniformLowBound=cvScalar(0,0,0,0);
    CvScalar UniformUpBound=cvScalar(255,255,255,0);
    cvRandArr(&rng,Image1,RandType1,UniformLowBound,UniformUpBound);
    cvRandArr(&rng,Image2,RandType2,LowBound,UpBound);

    CvScalar NormalMean=cvScalar(127,127,127,0);
    CvScalar NormalStandard =cvScalar(32,32,32,0);
    cvRandArr(&rng,Image2,RandType2,NormalMean,NormalStandard);

    cvNamedWindow("Random Uniform Distribution",0);
    cvShowImage("Random Uniform Distribution",Image1);
    cvNamedWindow("Random Normal Distribution",0);
    cvShowImage("Random Normal Distribution",Image2);
    cvWaitKey(0);
}

執行結果:


這個程式碼,使用到cvRandArr()這個函數,可以將IplImage資料結構及CvMat資料結構做隨機,而且可以給定隨機分佈的種類,一開始,先初始化Image1的圖形,並將它變成全黑,再複製一份給Image2,設定隨機種子,隨機分佈的種類,Image1給均勻分佈,Image2給常態分佈,給定純量數值(CvScalar),這邊,因為是用Channel為3的RGB圖形,因此cvScalar()的第四個參數不用填,而前三個參數為藍綠紅,在Image1均勻分布的部份是給它隨機的上界及下界,意思就是藍色0~255隨機,綠色0~255隨機,紅色0~255隨機,Image2常態分布的部份是給他平均數跟標準差,這邊平均數是127,標準差是32,標準差不能給太大,因為色彩空間的範圍是0~255,如果標準差給128的話2個標準差涵蓋的是68.26%的出現機率,會超出0~255的範圍,小於0的,大於255的,會各佔15.87%,因此,超出範圍的數字則會全部都卡在0跟255上,將會是錯誤的,要計算的話,最好預設範圍在6個標準差的大小,出現結果將會99%都在0~255的範圍內,常態分佈出現的會是鐘狀分配,均勻分佈則會呈現直線,詳細的內容就要參考統計學相關書籍啦.

均勻分佈                                                   常態分佈



常態分布的標準差
(1) (2)


cvRandArr()
矩陣(CvMat)或圖形(IplImage)隨機出現顏色,這可以模擬雜訊的實作.有兩種參數輸入方式
cvRandArr(CvRNG資料結構,IplImage或CvMat資料結構,均勻分佈參數,隨機範圍下限,隨機範圍上限)
cvRandArr(CvRNG資料結構,IplImage或CvMat資料結構,常態分佈參數,平均數,標準差)




OpenCV隨機的實作-基本隨機的應用

CvRNG,RNG代表的意思就是Random Number Generation,隨機數產生,隨機的部份實際上用起來很簡單,但原理的部份就稍微難一些了,基本的函式為cvRNG(),初始化資料結構,cvRandInt(),32bits隨機及浮點數隨機的cvRandReal(),以下程式碼實作.

簡單的隨機程式製作1
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>


int main()
{
    CvRNG rng;
    rng = cvRNG(cvGetTickCount());
    for(int i=0;i<100;i++)
    {
        printf("%d\n",cvRandInt(&rng));
    }
    printf("The Tick Frequency is %f\n",cvGetTickFrequency());
    system("pause");
}

執行結果:


這邊用cvGetTickCount()當種子與C語言time()不同的是,它雖然同樣用時間產生種子,但是它用的是int64長整數型別,而time()則是要看機器而定32位元電腦用32bits,64位元電腦則用64bits.這個程式產生了100個有正有負隨機均勻分佈的整數,cvRandInt()產生的是32bit隨機的數,用int去接的話會介於-2147483648~2147483647之間,而用unsign int去接的話會是0~4294967295,而printf()裡"%d"出來的型別是int,如果用printf("%d\n",cvRandInt(&rng)%256)的話出來的將會是0~255的正整數.

改成cvRandInt(&rng)%256之後


簡單的隨機程式製作2
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>


int main()
{
    CvRNG rng;
    rng = cvRNG(cvGetTickCount());
    for(int i=0;i<100;i++)
    {
        printf("%.2f\n",cvRandReal(&rng));
    }
    system("pause");
}

執行結果:


這裡cvRandReal()是拿cvRandInt()的結果乘以二的負三十二次方,因此兩個數會被轉成double去做除法運算而變成浮點數隨機的均勻分佈,如果這邊將printf("%.2f\n",cvRandReal(&rng))改成printf("%d\n",cvRandReal(&rng))亦會出現一堆整數數字,但會因為變數格式的關係隨機的意義將會失焦,因此,這邊程式不該用"%d"來列印,這個部份就要參考計算機組織與設計的計算機算數的部份.

cvGetTickCount()
回傳長整數64bits的時間數據,在OpenCV是為CvRNG而設的的專用種子.

cvGetTickFrequency()
回傳系統時脈頻率.

cvRNG()
跟一般C語言的srand()使用方法一樣,要先給它一個種子,但srand()用到的是unsigned int的32bits的種子範圍,而cvRNG()用的是LONGLONG型態,64bits大小的長整數種子,LONGLONG這個變數型別是在windows.h底下宣告的,也可以用long long表示.這邊初始化CvRNG資料結構,假如seed給0的話,它將會自動轉成-1.
cvRNG(64bits種子的數字)

cvRandInt()
傳回均勻分佈(Uniform Distribution),32bits的隨機數,均勻分佈為統計學上的專有名詞.表示長時間下所有數字出現的機率都是一樣的,而cvRandInt()在OpenCV裡使用的公式是

temp = (uint64)(unsigned)temp*1554115554 + (temp >> 32);

這公式(演算法)的名稱叫做Multiply-with-carry (MWC) generator,有興趣的話可以在網路上找"隨機數產生器"(Random Number Generator)可以找到一些資料,Multiply-with-carry是將64bits的種子去產生32bits的隨機數
unsigned cvRandInt(CvRNG資料結構)

cvRandReal()
傳回均勻分佈,0~1之間的隨機小數.cvRandReal()的公式則是用

cvRandInt(rng)*2.3283064365386962890625e-10

的方法,其實就是cvRandInt(rng)*2^(-32),也就是將cvRandInt()隨機出來的結果(32bits)除以2的三十二次方,因此,出現的結果將會是0~1之間的小數,也就是機率0~1之間.
double cvRandReal(CvRNG資料結構)



2008年6月23日 星期一

隨機,字型與繪圖

在這邊拿OpenCV的Sample code,"drawing.c"來做例子,這個程式碼播放的是很漂亮的動畫,而且大部分的字型函式及繪圖函式都被用到了,下面的程式碼已經被修改的跟"drawing.c"不太一樣了,其實執行結果除了給他隨機的Seed不同之外其他是相同的,增加了程式的易讀性,但是下面還是有用到一些技巧及資料結構需要去被理解的.

Drawing.c程式碼修改
#include <cv.h>
#include <highgui.h>


IplImage *Image1,*Image2;

CvScalar RandomColor(CvRNG* rng);
void DrawingLine(CvRNG *rng);
void DrawingRectangle(CvRNG *rng);
void DrawingEllipse(CvRNG *rng);
void DrawingPolyLine(CvRNG *rng);
void DrawingFillPoly(CvRNG *rng);
void DrawingCircle(CvRNG *rng);
void WritingText(CvRNG *rng);
void OpenCVForever(CvRNG *rng);

int main()
{
    CvRNG rng;
    rng = cvRNG(cvGetTickCount());

    CvSize ImageSize1 = cvSize(1000,700);
    Image1 = cvCreateImage(ImageSize1,8,3);
    cvZero(Image1);

    cvNamedWindow("Drawing",1);
    cvShowImage("Drawing",Image1);

    DrawingLine(&rng);
    DrawingRectangle(&rng);
    DrawingEllipse(&rng);
    DrawingPolyLine(&rng);
    DrawingFillPoly(&rng);
    DrawingCircle(&rng);
    WritingText(&rng);
    OpenCVForever(&rng);

    cvWaitKey(0);

}
CvScalar RandomColor(CvRNG* rng)
{
    int icolor = cvRandInt(rng);
    return CV_RGB(icolor&255,(icolor>>8)&255,(icolor>>16)&255);
}
void DrawingLine(CvRNG *rng)
{
    CvPoint FromPoint,ToPoint;
    CvScalar Color;
    int Thickness;
    for(int i=0;i<100;i++)
    {
        FromPoint = cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        ToPoint = cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);

        Color=RandomColor(rng);
        Thickness=cvRandInt(rng)%10;

        cvLine(Image1,FromPoint,ToPoint,Color,Thickness,CV_AA,1);

        cvShowImage("Drawing",Image1);
        cvWaitKey(5);
    }
}

void DrawingRectangle(CvRNG *rng)
{
    CvPoint VertexOne,VertexThree;
    CvScalar Color;
    int Thickness;
    for(int i=0;i<100;i++)
    {
        VertexOne=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        VertexThree=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);

        Color=RandomColor(rng);
        Thickness=cvRandInt(rng)%10-1;

        cvRectangle(Image1,VertexOne,VertexThree,Color,Thickness,CV_AA,0);
        cvShowImage("Drawing",Image1);
        cvWaitKey(5);
    }
}
void DrawingEllipse(CvRNG *rng)
{
    CvPoint CircleCenter;
    CvSize EllipseSize;
    double angle;
    double DrawStart;
    double DrawStop;
    CvScalar Color;
    int Thickness;
    for(int i =0;i<100;i++)
    {
        CircleCenter=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        EllipseSize=cvSize(cvRandInt(rng)%200,cvRandInt(rng)%200);

        angle = (cvRandInt(rng)%1000)*0.180;
        DrawStart=angle-100;
        DrawStop=angle+200;
        Color=RandomColor(rng);
        Thickness=cvRandInt(rng)%10-1;

        cvEllipse(Image1,CircleCenter,EllipseSize,angle,DrawStart,DrawStop,Color,Thickness,CV_AA,0);

        cvShowImage("Drawing",Image1);
        cvWaitKey(5);
    }
}

void DrawingPolyLine(CvRNG *rng)
{
    CvPoint PointArray1[6];
    CvPoint *PointArray[2]={&PointArray1[0],&PointArray1[3]};

    int PointNumber[2]={3,3};
    int BlockNumber=2;
    int IsClosed=1;
    CvScalar Color;
    int Thickness;
    for(int i=0;i<100;i++)
    {
        PointArray[0][0]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[0][1]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[0][2]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[1][0]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[1][1]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[1][2]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);

        Color=RandomColor(rng);
        Thickness=cvRandInt(rng)%10;

        cvPolyLine(Image1,PointArray,PointNumber,BlockNumber,IsClosed,Color,Thickness,CV_AA,0);

        cvShowImage("Drawing",Image1);
        cvWaitKey(5);
    }
}

void DrawingFillPoly(CvRNG *rng)
{
    CvPoint PointArray1[6];
    CvPoint *PointArray[2]={&PointArray1[0],&PointArray1[3]};

    int PointNumber[2]={3,3};
    int BlockNumber=2;

    CvScalar Color;

    for(int i=0;i<100;i++)
    {
        PointArray[0][0]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[0][1]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[0][2]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[1][0]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[1][1]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        PointArray[1][2]=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        Color=RandomColor(rng);

        cvFillPoly(Image1,PointArray,PointNumber,BlockNumber,Color,CV_AA,0);

        cvShowImage("Drawing",Image1);
        cvWaitKey(5);
    }
}
void DrawingCircle(CvRNG *rng)
{
    CvPoint CircleCenter;
    int Radius;
    CvScalar Color;
    int Thickness;
    for(int i=0;i<100;i++)
    {
        CircleCenter=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);

        Radius=cvRandInt(rng)%300;
        Color=RandomColor(rng);
        Thickness=cvRandInt(rng)%10-1;

        cvCircle(Image1,CircleCenter,Radius,Color,Thickness,CV_AA,0);
        cvShowImage("Drawing",Image1);

        cvWaitKey(5);
    }
}
void WritingText(CvRNG *rng)
{
    CvFont Font1;
    int FontFace;
    double HorizontalScale;
    double VerticalScale;
    double Shear;
    int Thickness;

    CvPoint TextPosition;     CvScalar Color;
    for(int i=1;i<100;i++)
    {
        TextPosition=cvPoint(cvRandInt(rng)%3000-1000,cvRandInt(rng)%2100-700);
        FontFace=cvRandInt(rng)%8;
        HorizontalScale=(cvRandInt(rng)%100)*0.05+0.1;
        VerticalScale=(cvRandInt(rng)%100)*0.05+0.1;
        Shear=(cvRandInt(rng)%5)*0.1;
        Thickness=cvRound(cvRandInt(rng)%10);
        Color=RandomColor(rng);

        cvInitFont(&Font1,FontFace,HorizontalScale,VerticalScale,Shear,Thickness,CV_AA);
        cvPutText(Image1,"GO GO GO!",TextPosition,&Font1,Color);

        cvShowImage("Drawing",Image1);
        cvWaitKey(5);
    }
}
void OpenCVForever(CvRNG *rng)
{
    CvFont Font1;
    CvSize TextSize;
    int Baseline = 0;
    CvPoint TextPosition;

    cvInitFont(&Font1,CV_FONT_HERSHEY_COMPLEX,3,3,0.0,5,CV_AA);
    cvGetTextSize("OpenCV forever!",&Font1,&TextSize,&Baseline );

    TextPosition =cvPoint((1000 - TextSize.width)/2,(700 + TextSize.height)/2);
    Image2 = cvCloneImage(Image1);

    for(int i = 0; i < 255; i++ )
    {
        cvSubS(Image2,cvScalarAll(i),Image1,0);

        cvPutText(Image1,"OpenCV forever!", TextPosition,&Font1,CV_RGB(255,i,i));

        cvShowImage("Drawing",Image1);
        cvWaitKey(5);
    }
}

執行結果:
video

這個解釋可多的呢,先宣告一個CvRNG的資料結構,為OpenCV的隨機資料結構,再用cvRNG()初始化,初始化的副程式內,給定了一個cvGetTickCount()OpenCV內建時間副程式來當做隨機副程式的種子,如果有用過C語言的srand()及rand()功能的話大致上應該能了解,如果不給定一個種子(Seed),則它隨機出來的數字是固定的,而它給定隨機種子的範圍,是正整數64bits的大小,也就是0~2^64數字大小,接著在開啟了一個1000*700大小的圖片顯示視窗,而它隨機的範圍是從3000*2100,讓隨機出來的圖型可以有那種不完整的感覺,接下來用分類的方式表達:

1.RandomColor()
回傳一個隨機的像素值,也就是R+G+B,這邊的隨機方式很有趣,因為他用到了一些小技巧,一張圖是由光的三原色紅綠藍所組合而成的,而紅綠藍三原色的範圍是0~255,它這邊所使用的技巧是AND運算以及位移運算,由cvRandInt()所回傳的數是32bits的整數,而實際上,三原色0~255所用到的位元數是8bits,所以,就直接對該數字做AND運算,可以得到他最後面8bits的數字,再對它做位移運算,往後移動8bits,再用AND運算擷取接下來的8bits,因此,32bits的隨機有16bits被RGB三原色用到啦,這個困難點在於,並不是所有的數字都是絕對的,它只適用2^n-1的數字時使用,也就是1,3,7,15,31,63,127,255...這個就要說到AND運算的原理啦,用起來沒有比cvRandInt()%256方便就是了,不過運算速度會比cvRandInt()%256還快.

2.DrawingLine()
這裡隨機產生了100條線段,繪製一條線的需求為給定一張IplImage格式的圖,線段起點座標,線段終點座標,線段顏色,線段的粗細程度,線段的類型(CV_AA),輸入的座標,粗細到小數點第幾位,這裡坐標,顏色,粗細給定一個隨機值來產生100條線段.

3.DrawingRectangle()
隨機產生100個方框,方框需要的是給定一個IplImage格式的圖,對角線的座標位置(頂點1跟頂點3),方框顏色,方框粗細,方框線條類型等,而這邊隨機了頂點,顏色,粗細.

4.DrawingEllipse()
隨機產生100個橢圓,橢圓需要IplImage的圖,圓心,橢圓的大小,旋轉角度,及他的繪圖起點到繪圖終點,如果說繪圖起點不是0,繪圖終點不是360,則它繪出來的圖會是一個弧形線段,有缺口的橢圓,再來是橢圓顏色,橢圓線條種類等,這邊角度,顏色,粗細都是隨機的.

5.DrawingPolyLine()
隨機產生100個多邊型,這邊多邊形的函式運用到一些小技巧,除了給定了IplImage資料結構之外,還要給定它點(CvPoint)陣列,也就是所有點的集合,把它存在一個以CvPoint宣告的一維陣列裡,但是這邊cvPolyLine()為什麼要給它的是二維陣列呢,因為這個函式,可以畫兩個以上的多邊型,而它可以存在好幾百個點,或好幾千的點(頂點),而這樣,就會降低了它的存取效能,這邊程式給它了最佳化的的方法,也就是設立Index,跟資料庫原理相同的,這邊設計是每3個陣列空間就做Index,加快點的一維陣列存取速度,其實說穿了也只是用二維陣列表示罷了但實際上,它的Index在多邊型cvPolyLine()是不具任何意義的,可以自由的設定Index的寬度.cvPolyLine()除了點陣列以外,還有多邊型點集合數目這邊的設定是{3,3}表示每三個點變成一個多邊形集合,也就是兩個三角形,如果把它改成PointNumber[1]={6}的話就是標準的六邊型了.再來cvPolyLine()下一個引數是區塊數(也就是多邊行內部對角線要被對角線劃分多少區塊),再一個引數為是否要被圍繞成一個多邊型(圖論的問題,路徑(path)跟路線(trail)),多邊型顏色,線條種類等,這邊點集合,顏色,粗細都是隨機的.

6.DrawingFillPoly()
隨機產生100個被填滿的多邊型,也就是實心的,非線段的,用IplImage資料結構,點集合陣列,組成多邊點集合數目,區塊數,填滿區域的顏色,邊線種類,輸入的點陣列,邊線等數據到小數點第幾位,而點集合,填滿區域的顏色為隨機產生的.

7.DrawingCircle()
隨機產生100個圓形,需要用到IplImage資料結構,圓心座標,半徑長度,圓形線段顏色,圓形粗細,線條種類,輸入座標,半徑長度,粗細數據到小數點第幾位,這邊圓心座標,半徑,圓形顏色,粗細都是隨機的.

8.WritingText()
產生100個文字訊息,要用到Font字型資料結構,先初始化Font資料結構,而cvInitFont()包含字型的種類,水平跟鉛直大小,文字的頃斜程度(Shear),文字的粗細寬度,線條種類,輸入水平,鉛直,頃斜,粗細的數據到小數點第幾位,這邊字型種類,水平鉛直,文字頃斜度,線條粗細都是隨機的. 在放置文字訊息,cvPutText()包含目標圖片,文字訊息,字型資料結構以及文字的顏色,而這邊顏色跟位置是隨機的.

9.OpenCVForever()
同8.這邊不同的是,用cvSubS()來做到灰階漸暗的效果,並且將文字訊息顯示在中間

2008年6月21日 星期六

GUI介面的製作-主控台介面的關閉

當你在做GUI介面的時候,會有黑底白字的命令及提示字元出現真的很討厭,明明已經是視窗介面了,為什麼還需要有黑框跑出來,這個問題在OpenCV的程式裡都是這樣執行的,也許久而久之就習慣了,不過,OpenCV還是可以做得到純GUI介面,需要用到的是"wincon.h"(Windows Console)主控台控制介面的函式庫,它包在"windows.h"裡,而實際上"highgui.h"就有包含"windows.h"的函式庫,直接用"highgui.h"就好了,而要關閉黑底白字的主控台介面其實很簡單,就只要用到FreeConsole()這個函式就好了,但是,主控台介面還是必要的,因為OpenCV視窗介面少了很多視窗介面該有的功能,如Label,Button,Eidt,Memo等文字輸入/顯示的設計,如果真的需要看到文字顯示資訊,或是輸入資料,主控台的存在是不可或缺的.當關閉主控台介面時,如果說遇到要暫時輸入文字資訊則在用AllocConsole()再度開啟主控台吧.

Webcam關閉主控台程式
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <windows.h>


int main()
{
    FreeConsole();

    CvCapture *capture;
    IplImage *frame;

    capture =cvCaptureFromCAM(0) ;

    cvNamedWindow("Webcam",0);
    while(true)
    {
        frame = cvQueryFrame(capture);

        cvShowImage("Webcam",frame);

        if(cvWaitKey(10)>=0)
        {
            break;
        }

    }
    cvReleaseCapture(&capture);
    cvDestroyWindow("Webcam");

}
執行結果:


這裡實際上存在一個很嚴重的問題,當把主控台介面關閉的時候,雖然只會出現圖形介面,但是,主控台的操控是在背景作業的,而開啟的視窗介面是一個執行緒,想了解執行緒是什麼則要去看作業系統原理等相關書籍,所以在這邊,如果是用cvWaitKey()的方法按鍵盤任意鍵結束視窗介面是不會有什麼問題的,可是如果是按GUI介面上右上角的關閉視窗,就會造成GUI介面這個執行緒被關閉,而實際上,背景作業的行程(process)仍在繼續執行,如果說是用圖片檔實作是沒什麼太大的問題,可是如果是用視訊播放程式的話,即使把GUI介面關閉,但是它裝置還是在繼續播放的.它仍然在跑while迴圈,仍然在不停的讀圖片,而這時的cvWaitKey()也失去作用了,因為cvWaitKey()是等待GUI介面上鍵盤事件的發生,而GUI介面這個執行緒被關閉了,就會變成只會卡記憶體(約8MB以上),每秒不斷造成分頁錯誤的程式了.

產生問題:每秒產生988個分頁錯誤(分頁錯誤差異),卡死記憶體8700KB(8MB)


要解決這樣的問題就要多加一個判斷,當視窗介面的執行緒被關閉的時候就要順便把行程也關閉,程式碼如下

Webcam關閉主控台程式2
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <windows.h>

int main()
{
    FreeConsole();

    CvCapture *capture;
    IplImage *frame;

    capture =cvCaptureFromCAM(0) ;

    cvNamedWindow("Webcam",0);
    while(true)
    {
        frame = cvQueryFrame(capture);

        cvShowImage("Webcam",frame);

        if(cvWaitKey(10)>=0)
        {
            break;
        }
        if(!cvGetWindowHandle("Webcam"))
        {
            break;
        }
    }
    cvReleaseCapture(&capture);
    cvDestroyWindow("Webcam");

}

這邊的判斷就只是多做了一個偵測"Webcam"這個視窗的HWND是否存在,如果不存在的話就跳出while迴圈,而程式才會繼續跳出while的下一步釋放記憶體並關閉這個程式的行程.

FreeConsole()
主控台介面背景作業

AllocConsole()
重新開啟主控台介面



2008年6月20日 星期五

GUI介面的製作-轉檔程式的實作

圖片檔案格式的轉換其實很簡單,應為所有轉換格式的演算法都包在cvLoadImage(),cvSaveImage()裡啦,對OpenCV而言,可以不用去知道如何去實作它的圖檔格式而做到直接開啟的功能,而他讀到的檔案甚至連它的相關資訊全都包在IplImage資料結構裡,以下是簡單的轉檔程式實作

圖檔格式的轉換
#include <cv.h>
#include <highgui.h>
#include <stdio.h>

int main()
{
    IplImage *Image1;
    char FileName[10],ConvertFormatAndName[10];

    printf("檔案名稱\n>");
    scanf("%s",&FileName);
    printf("轉檔名稱(jpg,bmp,tif,png,ppm,)\n>");
    scanf("%s",&ConvertFormatAndName);

    if((Image1=cvLoadImage(FileName,-1))!=0)
    {
        cvSaveImage(ConvertFormatAndName,Image1);
    }

}
執行結果:
(1)ghyll.jpg轉檔前

(2)執行轉檔程式

(3)轉檔後


非常的簡單又簡短吧!實際上也只用到cvLoadImage()跟cvSaveImage()的存檔讀檔的動作,比較複雜的一層已經被包在cvLoadImage()跟cvSaveImage()的副程式裡面了,當然,只用到單純檔案轉換根本不夠看,因為有很多應用軟體都可以做到檔案轉換的功能,但假使,今天有好幾千張的jpg圖檔要轉換成bmp圖檔,光是用軟體慢慢一張張開啟轉換就會累個半死,而有了這些source code就可以做更多更強大的應用.

將目錄下的所有圖檔做轉換
#include <stdio.h>
#include <stdlib.h>
#include <cv.h>
#include <highgui.h>
#include <string.h>
#include <dirent.h>

DIR *DIR1;
struct dirent *Entry1;
char *StrPointer;
char picFormat[4];

IplImage *Image1;
int main()
{
    printf("====轉檔程式====\n");
    printf("這是將跟執行檔同目錄底下的所有圖片包含\nbmp,dib\njpg,jpge,jpe,jp2\n");
    printf("exr\npng\ntif,tiff\npbm,pgm,ppm\nras,sr\n副檔名轉成目標檔案格式的程式\n");
    printf("\n\n請輸入要轉檔副檔名名稱\n");
    printf("(bmp,dib,jpg,jpge,jpe,jp2,exr,png,tif,tiff,pbm,pgm,ppm,ras,sr)\n\n> ");
    scanf(" %s",&picFormat);

    DIR1=opendir(".");
    while(Entry1=readdir(DIR1))
    {

        if((StrPointer=strstr(Entry1->d_name,".bmp"))||
           (StrPointer=strstr(Entry1->d_name,".dib"))||
           (StrPointer=strstr(Entry1->d_name,".jpg"))||
           (StrPointer=strstr(Entry1->d_name,".jpge"))||
           (StrPointer=strstr(Entry1->d_name,".jpe"))||
           (StrPointer=strstr(Entry1->d_name,".jp2"))||
           (StrPointer=strstr(Entry1->d_name,".exr"))||
           (StrPointer=strstr(Entry1->d_name,".png"))||
           (StrPointer=strstr(Entry1->d_name,".tif"))||
           (StrPointer=strstr(Entry1->d_name,".tiff"))||
           (StrPointer=strstr(Entry1->d_name,".pbm"))||
           (StrPointer=strstr(Entry1->d_name,".pgm"))||
           (StrPointer=strstr(Entry1->d_name,".ppm"))||
           (StrPointer=strstr(Entry1->d_name,".ras"))||
           (StrPointer=strstr(Entry1->d_name,".sr")))
        {

            Image1=cvLoadImage(Entry1->d_name,-1);

            strncpy(StrPointer+1,picFormat,3);

            cvSaveImage(Entry1->d_name,Image1);

            printf("%s\n",Entry1->d_name);
        }

    }

    system("pause");
}

執行結果:
(1)轉檔前

(2)選定副檔名

(3)執行列表

(4)轉檔後


這是將所有目錄底下的圖片,全部的圖檔格式轉成目標想要的圖檔,數量很大的時候跑程式會方便又省時,或是遇到一堆圖檔都開不起來的時候,也可以用這種方法將之轉換.
程式一開始用了"dirent.h"的函式庫,以及他函式庫底下DIR跟dirent(directory entry)資料結構,接著就是開啟dir目錄的列表,這邊用"."代表的是執行檔下的目錄位置,也就是相對位置,也可以用"c:\\xxx\"等路徑,也就是目錄底下的絕對位置,這邊開啟的結果會放在DIR資料結構內,再用readdir()檢視列表,將readdir()的結果放在dirent的資料結構內,有寫過資料庫相關程式設計的話,用法其實很相似的.它會逐次將目錄下的東西列出來,這邊擷取副檔名為跟圖形相關的檔案(bmp,jpg,png...)來做存取的動作,再用前面的scanf()輸入的副檔名名稱來做轉檔.

cvLoadImage()
被放在"highgui.h"裡,可以讀取不同圖檔格式,也可以藉由參數去轉全彩或灰階.
IplImage* cvLoadImage("讀取圖檔名稱",參數);

cvSaveImage()
被放在"highgui.h"裡,可以儲存成不同圖檔格式.
cvSaveImage("儲存圖檔名稱",IplImage資料結構);



2008年6月19日 星期四

GUI介面的製作-"cvcam.h"視訊函式庫(3)

"cvcam.h"也可以做AVI檔的播放,它也可以同樣利用方法呼叫來做frame的修改,而他同樣的,也是使用到IplImage的資料結構,這邊做一個播放檔案的程式

使用cvcam.h做AVI檔播放
#include <cvcam.h>
#include <cv.h>
#include <highgui.h>
#include <windows.h>

void CaptureCallback(IplImage *image);

int main()
{
    HWND CaptureWindow;
    cvNamedWindow("AVIplayer",0);
    CaptureWindow =(HWND)cvGetWindowHandle("AVIplayer");
    cvcamPlayAVI("Output.avi",CaptureWindow,640,480,(void *)&CaptureCallback);

    cvWaitKey(0);

}
void CaptureCallback(IplImage *frame)
{
}

執行結果:
video

在這邊,CaptureCallback內沒有任何程式碼,因為他的IplImage資料結構的Frame受到AVI檔案的格式影響,如果說要用一般Capture的檔案格式,則在檔案寫入時要選擇用全畫面(未壓縮),不過這樣付出的代價是很驚人的,不用幾秒鐘就超過50MB的檔案,而如果選擇壓縮格式的話,IplImage資料結構就要符合該壓縮格式的讀檔方式來對IplImage資料結構存取,因此,CaptureCallback()的框頁製作,要符合使用的壓縮格式來做畫面的修改.除了這個,cvcamPlayAVI()也提供了對HWND的支援,因此可以選定目標視窗來做AVI檔影片播放的動作.

cvcamPlayAVI()
播放AVI檔案的函式,它可以藉由自己定義的方法呼叫來修改frame的內容,但是要符合AVI壓縮檔案的檔案格式.假如檔案名稱的地方改成0,則他會出現開啟檔案的視窗由使用者選擇目標的AVI檔,
cvcamPlayAVI("檔案名稱",視窗ID,播放寬度,播放高度,自行定義的方法呼叫名稱);

再來用的是另一個AVI播放的函式,這個函式使用起來有點特別,有些地方要注意一下,這邊拿兩個AVI檔案播放來做比較.

cvcam.h的AVI播放函式
#include <cvcam.h>
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <windows.h>


void CaptureCallback(IplImage *image);
void onMouse(int Event,int x,int y,int flags,void *param);

cvcamAVIFILE cvcamAVIFILE1;
cvcamAVIFILE cvcamAVIFILE2;
int main()
{
    HWND CaptureWindow;
    cvNamedWindow("AVIplayer",0);
    CaptureWindow = (HWND)cvGetWindowHandle("AVIplayer");
    char AVIFileName1[]="Output.avi";
    char AVIFileName2[]="Output2.avi";

    cvcamAVIFILE1=cvcamAVIOpenFile(AVIFileName1);
    printf("%d\n",cvcamAVIFILE1);
    cvcamAVIFILE2=cvcamAVIOpenFile(AVIFileName2);
    printf("%d\n",cvcamAVIFILE2);

    cvcamAVISetWindow(1,(void *)CaptureWindow);
    cvcamAVISetCallback(1,(void *)&CaptureCallback);
    cvcamAVISetSize(1,320,240);

    cvSetMouseCallback("AVIplayer",onMouse,NULL);

    cvcamAVIRun(1);

    cvWaitKey(0);

    cvcamAVIStop(1);
    cvcamAVICloseFile(0);
    cvcamAVICloseFile(1);
}

void CaptureCallback(IplImage *frame)
{

}
void onMouse(int Event,int x,int y,int flags,void* param )
{
    if(Event==CV_EVENT_LBUTTONDOWN)
    {
        cvcamAVIPause(1);

    }
    else if(Event==CV_EVENT_RBUTTONDOWN)
    {
        cvcamAVIResume(1);
    }
}

執行結果:
video
(中間用滑鼠暫停了兩次)

為什麼要用兩個AVI檔案來測試呢?因為在"cvcam.h"內定義的使用格式為

int cvcamAVICloseFile(cvcamAVIFILE file);
int cvcamAVISetWindow(cvcamAVIFILE file, void* window);
int cvcamAVISetCallback(cvcamAVIFILE file, void* callback);
int cvcamAVISetSize(cvcamAVIFILE file, int width, int height);
int cvcamAVIRun(cvcamAVIFILE file);
int cvcamAVIStop(cvcamAVIFILE file);
int cvcamAVIPause(cvcamAVIFILE file);
int cvcamAVIResume(cvcamAVIFILE file);
int cvcamAVIWaitCompletion(cvcamAVIFILE file);
int cvcamAVIIsRunning(cvcamAVIFILE file);

這將會產生一個很嚴重的衝突,實際上,OpenCV的程式人員是想讓被開啟的AVI檔能像視訊播放程式的方式用裝置代碼表示,也就是,第一個avi檔出現的時候就用代號0,第二個就用代號1,但不幸的,直接把cvcamAVIFILE丟給它會給它的數字是1000,而第二個數字是1001,這將會造成AVI函式無法執行,而這邊的程式,就用第二的AVI檔來做影片播放,這邊的功能使用上也是很方便,跟前面的視訊播放一樣,可以開始,暫停,繼續,結束等操作,也可以藉由HWND來做嵌入視窗,這邊的設計就沿用了之前滑鼠事件的做法.而他的框頁修改的部份(CaptureCallback())也必須要參考AVI檔的檔案格式.

cvcamAVIOpenFile()
開啟AVI檔案,要用cvcamAVIFILE資料結構接收,但實際上,可以不需要用到cvcamAVIFILE資料結構就可以執行,cvcamAVIOpenFile()之後的程式只要輸入檔案代碼即可,就像視訊的cvcamGetCamerasCount()使用方法一樣.
cvcamAVIOpenFile("AVI檔案名稱");

cvcamAVICloseFile()
關閉AVI檔案,如果不做這個動作的話程式執行結束會當掉,參數要傳給它AVI檔案的代號.
cvcamAVICloseFile(AVI檔案代號)

cvcamAVISetWindow()
設定程式要嵌入的視窗,給它AVI檔案的代號,及視窗的HWND視窗ID.
cvcamAVISetWindow(AVI檔案代號,HWND視窗代號);

cvcamAVISetCallback()
可以藉由方法呼叫修改AVI檔的frame,但是要符合AVI檔的檔案格式.
cvcamAVISetCallback(AVI檔案代號,自行定義的副程式名稱);

cvcamAVISetSize()
設定AVI檔案的播放大小.
cvcamAVISetSize(AVI檔案代號,畫面寬度,畫面高度);

cvcamAVIRun()
選定目標要播放的AVI檔案
cvcamAVIRun(AVI檔案代號);

cvcamAVIStop()
停止目標AVI檔案的播放.
cvcamAVIStop(AVI檔案代號);

cvcamAVIPause()
暫停目標正在播放的AVI檔案.
cvcamAVIPause(AVI檔案代號);

cvcamAVIResume()
繼續執行目標被暫停的AVI檔案.
cvcamAVIResume(AVI檔案代號);

--
附註:Opencv1.0 only,如果opencv 1.1想要使用的話,可以到sourceforge的opencv網站上下載

Copyright 2008-2009,yester