2008年7月16日 星期三

資料結構操作與運算-IplImage,CvMat的空間轉換

這邊主要實作的是,將IplImage圖形資料空間(imageData)轉成CvMat空間(data),或是將CvMat矩陣空間轉成IplImage的圖形空間.

IplImage,CvMat圖形矩陣空間
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


int main()
{
    IplImage *Image1;
    IplImage *Image2;
    CvMat *Matrix1;

    CvSize Size1;
    Image1=cvLoadImage("footpath.jpg",1);

    Size1=cvGetSize(Image1);

    Image2=cvCreateImageHeader(Size1,IPL_DEPTH_8U,3);
    Matrix1=cvCreateMatHeader(Size1.height,Size1.width,CV_8UC3);

    cvGetMat(Image1,Matrix1);

    cvFlip(Matrix1,Matrix1,1);

    cvGetImage(Matrix1,Image2);

    cvNamedWindow("footpath (Flip)",1);
    cvShowImage("footpath (Flip)",Image2);
    cvWaitKey(0);

    cvReleaseImage(&Image1);
    cvReleaseImageHeader(&Image2);
    cvReleaseMatHeader(&Matrix1);
    cvDestroyWindow("footpath (Flip)");
}

原始圖片:


執行結果:


這邊的執行結果是將圖形做翻轉(Flip)的動作,利用標頭給定標頭轉換CvMat資料結構矩陣空間及IplImage的矩陣空間,但實際上它們的矩陣空間是共用的,Image1的矩陣空間跟Image2跟CvMat共用相同的記憶體位址,都是同一個imageData或CvMat的data,而cvFlip()他可以對CvMat及IplImage資料結構進行翻轉.cvFlip()第三個參數如果數值為0,為水平翻轉,數值為1,為垂直翻轉,數值為-1則是水平+垂直翻轉

IplImage,CvMat圖形矩陣空間2
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


int main()
{
    IplImage *Image1;
    IplImage *Image2;
    CvMat *ROIMatrix;
    CvRect Rect1;

    CvSize Size1;
    Image1=cvLoadImage("footpath.jpg",1);

    Size1=cvGetSize(Image1);
    Image2=cvCreateImage(Size1,IPL_DEPTH_8U,3);
    ROIMatrix=cvCreateMat(150,150,CV_8UC3);

    Rect1=cvRect(300,150,150,150);
    cvGetSubRect(Image1,ROIMatrix,Rect1);
    cvRepeat(ROIMatrix,Image2);

    cvNamedWindow("Image Repeat",1);
    cvShowImage("Image Repeat",Image2);
    cvWaitKey(0);

    cvReleaseImage(&Image1);
    cvReleaseImageHeader(&Image2);
    cvReleaseMat(&ROIMatrix);
    cvDestroyWindow("Image Repeat");
}

執行結果:


這邊是以ROI的方式由IplImage資料結構取得CvMat資料結構150*150的區塊,而cvGetSubRect()出來的結果,ROIMatrix跟Image1共用記憶體空間,只不過ROIMatrix取得了Rect資料結構所選擇的區段,而用了cvRepeat(),Image2則是沒有跟Image1跟ROIMatrix共用記憶體區段.

在這邊,前兩個程式碼矩陣空間標頭宣告都是CV_8U3C,而它因為是矩陣空間(RawData)的轉移,所以它的內部資料空間的型別並不會因為標頭宣告而改變,在圖形,矩陣上的空間使用的型別都會是uchar,也可以直接將標頭矩陣的種類改成CV_32FC3,但是這樣會造成標頭資訊與內儲資料空間不符,因此不建議程式這麼做,而如果要改變矩陣空間的型別,可以使用cvConvertScale()來做轉換.

IplImage的空間型別轉換實作
#include <cv.h>
#include <highgui.h>
#include <stdio.h>


int Scale=1;
int Shift=0;

int main()
{
    IplImage *Image1;
    IplImage *Image2;
    IplImage *Image3;

    CvSize Size1;
    Image1=cvLoadImage("footpath.jpg",1);
    Size1=cvGetSize(Image1);
    Image2=cvCreateImage(Size1,IPL_DEPTH_32F,3);
    Image3=cvCreateImage(Size1,IPL_DEPTH_8U,3);

    cvConvertScale(Image1,Image2,Scale,Shift);

    printf("%.1f ",cvGet2D(Image2,50,50).val[0]);
    printf("%.1f ",cvGet2D(Image2,50,50).val[1]);
    printf("%.1f ",cvGet2D(Image2,50,50).val[2]);

    Shift=-100;
    cvConvertScale(Image2,Image3,Scale,Shift);

    cvNamedWindow("footpath",1);
    cvShowImage("footpath",Image3);
    cvWaitKey(0);

    cvReleaseImage(&Image1);
    cvReleaseImage(&Image2);
    cvReleaseImage(&Image3);
    cvDestroyWindow("footpath");
}

執行結果:


cvConvertScale()可以對任何圖形空間的型別來做轉換,因此不會因為uchar型別0~255的限制而超出範圍了,而cvConvertScale()亦可以使用再IplImage資料結構及CvMat資料結構上,但必須要與原圖的通道數及大小一致,第一個引數目標圖形,第二個引數為輸出圖形,第三個為目標圖形純量積相乘,第四的為全部顏色位移量,公式為

OutputPic[i][j]=Scale*Convert(InputPic[i][j])+Shift   for all i=0,1,2,3...height,j=0,1,2,3...width

不過,要記得將轉換型別的格式由浮點樹形別轉換回uchar,要不然cvShowImage()就無法顯示,而cvSaveImage()執行出來的圖檔也會是錯誤的.

(1)IplImage,CvMat資料結構共用圖形記憶體空間


(2)cvGetSubRect()擷取記憶體區段

前面IplImage擷取ROI區段也是同樣的方式

cvGetMat()
給定CvMat結構的標頭,可以取得目標IplImage結構或是CvMat結構的圖形資料空間.而與目標結構共用記憶體.
cvGetMat(目標IplImage結構或CvMat結構,CvMat標頭結構)

cvGetImage()
給定IplImage資料結構的標頭,可以取得目標IplImage結構或是CvMat結構的圖形資料空間.而與目標結構共用記憶體.
cvGetImage(目標IplImage結構或CvMat結構,IplImage標頭結構)

cvFlip()
對目標IplImage資料結構或CvMat資料結構做翻轉的動作,第一個引數為目標IplImage資料結構或CvMat結構,第二個為翻轉後放置結果的IplImage或CvMat資料結構,第三個引數為翻轉方式,0為水平翻轉,1為垂直翻轉,-1為水平+垂直翻轉.
cvFlip(目標IplImage或CvMat資料結構,輸出IplImage或CvMat資料結構,翻轉方式數據)

cvGetSubRect()
擷取區塊子圖,跟ROI使用方式很類似,擷取出來的結果會放在CvMat資料結構上,第一個引數為目標IplImage資料結構或CvMat資料結構,第二個引數為輸出的CvMat資料結構,第三的引數為選定的位置跟大小的CvRect資料結構.
cvGetSubRect(目標IplImage或CvMat資料結構,輸出的CvMat資料結構,選定的CvRect資料結構)

cvRepeat()
重複目標圖片,直到被選定的結構空間填滿為止,這邊兩個引數都可以用IplImage或CvMat資料結構,而也可以第一張圖比第二張大,但是看不到重複的效果.
cvRepeat(目標IplImage或CvMat資料結構,輸出IplImage資料結構或CvMat結構)

cvConvertScale()
可以任意轉換矩陣資料空間游標頭檔定義的的型別,可以用IplImage資料結構及CvMat資料結構,第一個引數為目標結構,第二個引數為輸出結構,第三個為矩陣空間的純量積(Scalar multiplication),也就是對矩陣乘上某倍數,第四個為數據大小的位移,也就是對每個矩陣的數據加上某數據
cvConvertScale(目標IplImage資料結構或CvMat結構,輸出IplImage結構或CvMat結構,矩陣純量數據,矩陣位移數據)



1 意見:

滄君 提到...

您好,我是根據您的網站一一往下學習CV的學生,再到這二題的時候(也就是重複貼上的那題),我將您的程式完整複製過去執行,出現了以下錯誤:
於 0x7645c41f 的 text.exe 中第一個可能發生的例外狀況: Microsoft C++ 在記憶體位置 0x0047f948,發生例外狀況: cv::Exception

我只能猜測跟記憶體有關,一行行檢查發現出錯在cvRepeat執行時,我完全沒有改動您的程式,想要請問這樣的問題該怎麼解決,我是不是在其他地方做錯什麼?

Copyright 2008-2009,yester