日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV实现基于傅里叶变换(FFT)的旋转文本校正(文字方向检测)

發布時間:2025/7/25 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV实现基于傅里叶变换(FFT)的旋转文本校正(文字方向检测) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

OpenCV實現基于傅里葉變換的旋轉文本校正

from: http://johnhany.net/2013/11/dft-based-text-rotation-correction/

發布于 2013年11月25日11,222次閱讀

代碼

? ? ? ??先給出代碼,再詳細解釋一下過程:


123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160#include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>using namespacecv;usingnamespacestd;#define GRAY_THRESH 150#define HOUGH_VOTE 100//#define DEGREE 27int main(intargc,char**argv){//Read a single-channel imageconstchar*filename="imageText.jpg";Mat srcImg=imread(filename,CV_LOAD_IMAGE_GRAYSCALE);if(srcImg.empty())return-1;imshow("source",srcImg);Pointcenter(srcImg.cols/2,srcImg.rows/2);#ifdef DEGREE//Rotate source imageMatrotMatS=getRotationMatrix2D(center,DEGREE,1.0);warpAffine(srcImg,srcImg,rotMatS,srcImg.size(),1,0,Scalar(255,255,255));imshow("RotatedSrc",srcImg);//imwrite("imageText_R.jpg",srcImg);#endif//Expand image to an optimal size, for faster processing speed//Set widths of borders in four directions//If borderType==BORDER_CONSTANT, fill the borders with (0,0,0)Mat padded;intopWidth=getOptimalDFTSize(srcImg.rows);intopHeight=getOptimalDFTSize(srcImg.cols);copyMakeBorder(srcImg,padded,0,opWidth-srcImg.rows,0,opHeight-srcImg.cols,BORDER_CONSTANT,Scalar::all(0));Matplanes[]={Mat_<float>(padded),Mat::zeros(padded.size(),CV_32F)};Mat comImg;//Merge into a double-channel imagemerge(planes,2,comImg);//Use the same image as input and output,//so that the results can fit in Mat welldft(comImg,comImg);//Compute the magnitude//planes[0]=Re(DFT(I)), planes[1]=Im(DFT(I))//magnitude=sqrt(Re^2+Im^2)split(comImg,planes);magnitude(planes[0],planes[1],planes[0]);//Switch to logarithmic scale, for better visual results//M2=log(1+M1)Mat magMat=planes[0];magMat+=Scalar::all(1);log(magMat,magMat);//Crop the spectrum//Width and height of magMat should be even, so that they can be divided by 2//-2 is 11111110 in binary system, operator & make sure width and height are always evenmagMat=magMat(Rect(0,0,magMat.cols&-2,magMat.rows&-2));//Rearrange the quadrants of Fourier image,//so that the origin is at the center of image,//and move the high frequency to the cornersintcx=magMat.cols/2;intcy=magMat.rows/2;Matq0(magMat,Rect(0,0,cx,cy));Mat q1(magMat,Rect(0,cy,cx,cy));Matq2(magMat,Rect(cx,cy,cx,cy));Mat q3(magMat,Rect(cx,0,cx,cy));Mat tmp;q0.copyTo(tmp);q2.copyTo(q0);tmp.copyTo(q2);q1.copyTo(tmp);q3.copyTo(q1);tmp.copyTo(q3);//Normalize the magnitude to [0,1], then to[0,255]normalize(magMat,magMat,0,1,CV_MINMAX);MatmagImg(magMat.size(),CV_8UC1);magMat.convertTo(magImg,CV_8UC1,255,0);imshow("magnitude",magImg);//imwrite("imageText_mag.jpg",magImg);//Turn into binary imagethreshold(magImg,magImg,GRAY_THRESH,255,CV_THRESH_BINARY);imshow("mag_binary",magImg);//imwrite("imageText_bin.jpg",magImg);//Find lines with Hough Transformationvector<Vec2f>lines;floatpi180=(float)CV_PI/180;Mat linImg(magImg.size(),CV_8UC3);HoughLines(magImg,lines,1,pi180,HOUGH_VOTE,0,0);intnumLines=lines.size();for(intl=0;l<numLines;l++){floatrho=lines[l][0],theta=lines[l][1];Point pt1,pt2;doublea=cos(theta),b=sin(theta);doublex0=a*rho,y0=b*rho;pt1.x=cvRound(x0+1000*(-b));pt1.y=cvRound(y0+1000*(a));pt2.x=cvRound(x0-1000*(-b));pt2.y=cvRound(y0-1000*(a));line(linImg,pt1,pt2,Scalar(255,0,0),3,8,0);}imshow("lines",linImg);//imwrite("imageText_line.jpg",linImg);if(lines.size()==3){cout<<"found three angels:"<<endl;cout<<lines[0][1]*180/CV_PI<<endl<<lines[1][1]*180/CV_PI<<endl<<lines[2][1]*180/CV_PI<<endl<<endl;}//Find the proper angel from the three found angelsfloatangel=0;floatpiThresh=(float)CV_PI/90;floatpi2=CV_PI/2;for(intl=0;l<numLines;l++){floattheta=lines[l][1];if(abs(theta)<piThresh||abs(theta-pi2)<piThresh)continue;else{angel=theta;break;}}//Calculate the rotation angel//The image has to be square,//so that the rotation angel can be calculate rightangel=angel<pi2?angel:angel-CV_PI;if(angel!=pi2){floatangelT=srcImg.rows*tan(angel)/srcImg.cols;angel=atan(angelT);}floatangelD=angel*180/(float)CV_PI;cout<<"the rotation angel to be applied:"<<endl<<angelD<<endl<<endl;//Rotate the image to recoverMatrotMat=getRotationMatrix2D(center,angelD,1.0);Mat dstImg=Mat::ones(srcImg.size(),CV_8UC3);warpAffine(srcImg,dstImg,rotMat,srcImg.size(),1,0,Scalar(255,255,255));imshow("result",dstImg);//imwrite("imageText_D.jpg",dstImg);waitKey(0);return0;}

過程
讀取圖片

Mat srcImg = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);if(srcImg.empty())return -1;
123MatsrcImg=imread(filename,CV_LOAD_IMAGE_GRAYSCALE);if(srcImg.empty())return-1;

? ? ? ? srcImg.empty()用來判斷是否成功讀進圖像,如果srcImg中沒有數據,在后面的步驟會產生內存錯誤。
? ? ? ? 由于處理的是文本,彩色信息不會提供額外幫助,所以要用CV_LOAD_IMAGE_GRAYSCALE表明以灰度形式讀進圖像。
? ? ? ? 假定讀取的圖像如下:

旋轉原圖像(可選)

Point center(srcImg.cols/2, srcImg.rows/2);#ifdef DEGREE//Rotate source imageMat rotMatS = getRotationMatrix2D(center, DEGREE, 1.0);warpAffine(srcImg, srcImg, rotMatS, srcImg.size(), 1, 0, Scalar(255,255,255));imshow("RotatedSrc", srcImg);//imwrite("H:\\imageText_02_R.jpg",srcImg);#endif
123456789Pointcenter(srcImg.cols/2,srcImg.rows/2);#ifdef DEGREE//Rotate source imageMatrotMatS=getRotationMatrix2D(center,DEGREE,1.0);warpAffine(srcImg,srcImg,rotMatS,srcImg.size(),1,0,Scalar(255,255,255));imshow("RotatedSrc",srcImg);//imwrite("H:\\imageText_02_R.jpg",srcImg);#endif

? ? ? ? 如果手頭沒有這樣的傾斜圖像,可以選擇一張正放的文本圖像,再把第12行#define DEGREE那行前的注釋符號去掉。然后這部分代碼就會把所給的圖像旋轉你規定的角度,再交給后面處理。

圖像延擴

Mat padded;int opWidth = getOptimalDFTSize(srcImg.rows);int opHeight = getOptimalDFTSize(srcImg.cols);copyMakeBorder(srcImg, padded, 0, opWidth-srcImg.rows, 0, opHeight-srcImg.cols, BORDER_CONSTANT, Scalar::all(0));
1234Matpadded;int opWidth=getOptimalDFTSize(srcImg.rows);intopHeight=getOptimalDFTSize(srcImg.cols);copyMakeBorder(srcImg,padded,0,opWidth-srcImg.rows,0,opHeight-srcImg.cols,BORDER_CONSTANT,Scalar::all(0));

? ? ? ? OpenCV中的DFT采用的是快速算法,這種算法要求圖像的尺寸是2、3和5的倍數時處理速度最快。所以需要用getOptimalDFTSize()找到最適合的尺寸,然后用copyMakeBorder()填充多余的部分。這里是讓原圖像和擴大的圖像左上角對齊。填充的顏色如果是純色對變換結果的影響不會很大,后面尋找傾斜線的過程又會完全忽略這一點影響。

DFT

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

? ? ? ? DFT要分別計算實部和虛部,把要處理的圖像作為輸入的實部、一個全零的圖像作為輸入的虛部。dft()輸入和輸出應該分別為單張圖像,所以要先用merge()把實虛部圖像合并,分別處于圖像comImg的兩個通道內。計算得到的實虛部仍然保存在comImg的兩個通道內。

獲得DFT圖像

split(comImg, planes);magnitude(planes[0], planes[1], planes[0]);Mat magMat = planes[0];magMat += Scalar::all(1);log(magMat, magMat);
123456split(comImg,planes);magnitude(planes[0],planes[1],planes[0]);Mat magMat=planes[0];magMat+=Scalar::all(1);log(magMat,magMat);

? ? ? ? 一般都會用幅度圖像來表示圖像傅里葉的變換結果(傅里葉譜)。
? ? ? ? 幅度的計算公式:magnitude = sqrt(Re(DFT)^2 + Im(DFT)^2)。
? ? ? ? 由于幅度的變化范圍很大,而一般圖像亮度范圍只有[0,255],容易造成一大片漆黑,只有幾個點很亮。所以要用log函數把數值的范圍縮小。

?

magMat = magMat(Rect(0, 0, magMat.cols & -2, magMat.rows & -2));int cx = magMat.cols/2;int cy = magMat.rows/2;Mat q0(magMat, Rect(0, 0, cx, cy));Mat q1(magMat, Rect(0, cy, cx, cy));Mat q2(magMat, Rect(cx, cy, cx, cy));Mat q3(magMat, Rect(cx, 0, cx, cy));Mat tmp;q0.copyTo(tmp);q2.copyTo(q0);tmp.copyTo(q2);q1.copyTo(tmp);q3.copyTo(q1);tmp.copyTo(q3);normalize(magMat, magMat, 0, 1, CV_MINMAX);Mat magImg(magMat.size(), CV_8UC1);magMat.convertTo(magImg,CV_8UC1,255,0);
12345678910111213141516171819202122magMat=magMat(Rect(0,0,magMat.cols&-2,magMat.rows&-2));intcx=magMat.cols/2;int cy=magMat.rows/2;Mat q0(magMat,Rect(0,0,cx,cy));Matq1(magMat,Rect(0,cy,cx,cy));Mat q2(magMat,Rect(cx,cy,cx,cy));Matq3(magMat,Rect(cx,0,cx,cy));Mattmp;q0.copyTo(tmp);q2.copyTo(q0);tmp.copyTo(q2);q1.copyTo(tmp);q3.copyTo(q1);tmp.copyTo(q3);normalize(magMat,magMat,0,1,CV_MINMAX);MatmagImg(magMat.size(),CV_8UC1);magMat.convertTo(magImg,CV_8UC1,255,0);

? ? ? ? dft()直接獲得的結果中,低頻部分位于四角,高頻部分位于中間。習慣上會把圖像做四等份,互相對調,使低頻部分位于圖像中心,也就是讓頻域原點位于中心。

?

? ? ? ? 雖然用log()縮小了數據范圍,但仍然不能保證數值都落在[0,255]之內,所以要先用normalize()規范化到[0,1]內,再用convertTo()把小數映射到[0,255]內的整數。結果保存在一幅單通道圖像內:

?

Hough直線檢測
? ? ? ? 從傅里葉譜可以明顯地看到一條過中心點的傾斜直線。要想求出這個傾斜角,首先要在圖像上找出這條直線。
? ? ? ? 一個很方便的方法是采用霍夫(Hough)變換檢測直線。

threshold(magImg,magImg,GRAY_THRESH,255,CV_THRESH_BINARY);
1threshold(magImg,magImg,GRAY_THRESH,255,CV_THRESH_BINARY);

? ? ? ? Hough變換要求輸入圖像是二值的,所以要用threshold()把圖像二值化。
? ? ? ? 二值化的一種結果:

vector<Vec2f> lines;float pi180 = (float)CV_PI/180;Mat linImg(magImg.size(),CV_8UC3);HoughLines(magImg,lines,1,pi180,HOUGH_VOTE,0,0);int numLines = lines.size();for(int l=0; l<numLines; l++){float rho = lines[l][0], theta = lines[l][1];Point pt1, pt2;double a = cos(theta), b = sin(theta);double x0 = a*rho, y0 = b*rho;pt1.x = cvRound(x0 + 1000*(-b));pt1.y = cvRound(y0 + 1000*(a));pt2.x = cvRound(x0 - 1000*(-b));pt2.y = cvRound(y0 - 1000*(a));line(linImg,pt1,pt2,Scalar(255,0,0),3,8,0);}
1234567891011121314151617vector<Vec2f>lines;float pi180=(float)CV_PI/180;MatlinImg(magImg.size(),CV_8UC3);HoughLines(magImg,lines,1,pi180,HOUGH_VOTE,0,0);intnumLines=lines.size();for(intl=0;l<numLines;l++){floatrho=lines[l][0],theta=lines[l][1];Pointpt1,pt2;doublea=cos(theta),b=sin(theta);doublex0=a*rho,y0=b*rho;pt1.x=cvRound(x0+1000*(-b));pt1.y=cvRound(y0+1000*(a));pt2.x=cvRound(x0-1000*(-b));pt2.y=cvRound(y0-1000*(a));line(linImg,pt1,pt2,Scalar(255,0,0),3,8,0);}

? ? ? ? 這一部分用HoughLines()檢測圖像中可能存在的直線,并把直線參數保存在向量組lines中,然后繪制出找到的直線。
? ? ? ? 兩個參數GRAY_THRESH和HOUGH_VOTE需要手動指定,不同的圖像需要設置不同的參數,同一段文本旋轉不同的角度也需要不同的參數。GRAY_THRESH越大,二值化的閾值就越高;HOUGH_VOTE越大,霍夫檢測的投票數就越高(需要更多的共線點來確定一條直線)。說白了,如果發現二值化圖像中直線附近有很多散點,就要適當提高GRAY_THRESH;如果發現從二值圖像的一條直線上檢測到了幾條角度相差很小的直線,就需要適當提高HOUGH_VOTE。我們希望得到的結果時剛好檢測到三條直線(有時只能檢測到一條直線,后面會給出一個例子)。
? ? ? ? 檢測到的直線:

計算傾斜角
? ? ? ? 上面得到了三個角度,一個是0度,一個是90度,另一個就是我們所需要的傾斜角。要把這個角找出來,而且要考慮誤差。

float angel=0;float piThresh = (float)CV_PI/90;float pi2 = CV_PI/2;for(int l=0; l<numLines; l++){float theta = lines[l][1];if(abs(theta) < piThresh || abs(theta-pi2) < piThresh)continue;else{angel = theta;break;}}angel = angel<pi2 ? angel : angel-CV_PI;if(angel != pi2){float angelT = srcImg.rows*tan(angel)/srcImg.cols;angel = atan(angelT);}float angelD = angel*180/(float)CV_PI;
1234567891011121314151617181920floatangel=0;float piThresh=(float)CV_PI/90;floatpi2=CV_PI/2;for(intl=0;l<numLines;l++){floattheta=lines[l][1];if(abs(theta)<piThresh||abs(theta-pi2)<piThresh)continue;else{angel=theta;break;}}angel=angel<pi2?angel:angel-CV_PI;if(angel!=pi2){floatangelT=srcImg.rows*tan(angel)/srcImg.cols;angel=atan(angelT);}float angelD=angel*180/(float)CV_PI;

? ? ? ? 由于DFT的特點,只有輸入圖像是正方形時,檢測到的角才是文本真正旋轉的角度。但我們的輸入圖像不一定是正方形的,所以要根據圖像的長寬比改變這個角度。
? ? ? ? 還有一個需要注意的細節,雖然HoughLines()輸出的傾斜角在[0,180)之間,但在[0,90]和(90,180)之間這個角的含義是不同的。請看圖示:

?

? ? ? ? 當傾斜角大于90度時,(180-傾斜角)才是直線相對豎直方向的偏離角度。在OpenCV中,逆時針旋轉,角度為正。要把圖像轉回去,這個角度就變成了(傾斜角-180)。
校正圖像
? ? ? ? 最后一步,當然是把圖像轉回去~

Mat rotMat = getRotationMatrix2D(center,angelD,1.0);Mat dstImg = Mat::ones(srcImg.size(),CV_8UC3);warpAffine(srcImg,dstImg,rotMat,srcImg.size(),1,0,Scalar(255,255,255));
123MatrotMat=getRotationMatrix2D(center,angelD,1.0);Mat dstImg=Mat::ones(srcImg.size(),CV_8UC3);warpAffine(srcImg,dstImg,rotMat,srcImg.size(),1,0,Scalar(255,255,255));

?

? ? ? ? 先用getRotationMatrix2D()獲得一個2*3的仿射變換矩陣,再把這個矩陣輸入warpAffine(),做一個單純旋轉的仿射變換。warpAffine()的最后一個參數Scalar(255,255,255)是把由于旋轉產生的空白用白色填充。
? ? ? ? 校正的結果:


一個檢測單條直線的例子
原始圖像:

傅里葉譜:

? ? ? ? 只有一條明顯的直線。還好僅有的這條直線正是我們所需要的。
檢測直線:

校正結果:


對中文的效果
? ? ? ? 我們來試試看這段程序對中文的校正效果。
輸入圖像:

傅里葉譜:

? ? ? ? 可以發現有許多條平行的亮線,其中過頻域原點的那條長度最長,最容易檢測出來。
檢測直線:

校正結果:

? ? ? ? 雖然中文和英文在文字上有很大的不同,但字母(或者文字)的高度比較一致,使得行與行之間的分隔很明顯。所以它們的頻域特征是相似的。


對其他語言文字的效果
? ? ? ? 我從IMDB.com摘取影片《教父》的英文介紹,然后用谷歌翻譯成其他文字進行測試。
阿拉伯語

一枚反例
老撾語:

傅里葉譜:

一種二值化的結果:

直線檢測:

? ? ? ? 這種文字的很多字母的上下方多了很多“筆畫”(我不知道該怎么稱呼那些小曲線),讓行與行之間的分離變得不明顯,使得頻域特征變得不明顯。
? ? ? ? 雖然用肉眼可以看出傅里葉譜中存在一條傾斜的直線,但它的亮度太低,二值化過程很難排除噪聲,導致直線檢測會首先檢出噪聲產生的直線。這也是我的程序目前受限之處。需要增加一個過濾散點噪聲的步驟以增加程序的適用范圍。


參考:Discrete Fourier Transform — OpenCV 2.4.7.0 documentation

代碼還可以在這里下載:https://github.com/johnhany/textRotCorrect


2014.1.3更新:

? ? ? ? 由于文章內的圖片右下角存在水印,若直接使用文章內的圖片進行處理會使頻域原點附近增加一團亮點,妨礙直線的檢出。而且為了節省空間,圖片是經過縮小的,使得字母的邊緣變得模糊,頻域特征也減弱。為此我提供了十幅沒有水印的圖片,供想要親手實驗的朋友使用。下載鏈接

類別:圖像處理標簽:opencv,文本處理

文章導航

N條線段求交的掃描線算法Java+MySQL實現網絡爬蟲程序

共有21條評論

  • ma說道:2016年5月31日 上午11:43

    您好,請問一下您使用的這個方法是有沒有發過相應的文章,因為我的實驗里用到了您的方法,論文的時候需要寫這個方法的來源,所以想問一下您這個方法來自您自己發過的文章里的還是其他書本或期刊上的。謝謝您。

    回復
  • John Hany說道:2016年6月2日 上午11:14

    我的方法并不是完全原創,參考的資料也只有文章末尾的那個官方文檔頁面。那個頁面的最后一節只是提到水平與傾斜的文本頻譜圖像是有差異的,也沒有提及相關的文獻,我也是自己用代碼實驗了一下,所以也沒有發過相應的文章。某些雜志是允許把網址作為參考文獻的,可以按照格式要求直接把這篇文章的鏈接寫上。

    回復
  • ma說道:2016年7月13日 上午11:05

    謝謝。

    回復
  • ma說道:2016年5月28日 下午6:45

    您好,看了您的方法后,我用C語言試用了一下,但是不知道為什么在HoughLines后,呈現出來的圖象是全黑的。為了方便,我貼出這部分的代碼,不知道哪里出了問題,想向您請教一下。謝謝。

    ?

    CvMemStorage *storage? = cvCreateMemStorage(0);
    ?CvSeq* lines = NULL;
    ?float pi180 = (float)CV_PI/180;
    ?lines =?cvHoughLines2(Image,storage,CV_HOUGH_STANDARD,1,pi180,HOUGH_VOTE,0,0); //Image是之前的二值化圖象
    IplImage *lineImg;
    ?lineImg = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
    ?int numLines = lines->total;
    ?int i=0;
    ?for(i=0;i<numLines;i++)
    ? ?{
    ? ? ?float *line = (float*) cvGetSeqElem(lines,i);
    ? ? ?float rho = line[0];
    ? ? ?float theta = line[1];
    ? ? ?CvPoint pt1,pt2;
    ? ? ?double a = cos(theta), b = sin(theta);
    ? ? ?double x0 = a*rho, y0 = b*rho;
    ? ? ?pt1.x = cvRound(x0+1000*(-b));
    ? ? ?pt1.y = cvRound(y0+1000*(a));
    ? ? ?pt2.x = cvRound(x0-1000*(-b));
    ? ? ?pt2.y = cvRound(y0-1000*(a));
    ? ? ?cvLine(lineImg,pt1,pt2,CV_RGB(255,0,0),3,8,0);
    }
    ? ? cvNamedWindow("line",0);
    ? ? cvShowImage("line",lineImg);
    ? ? cvWaitKey(10000);

    回復
  • John Hany說道:2016年6月2日 上午11:15

    抱歉,最近事情比較多,現在問題解決了嗎?

    回復
  • ma說道:2016年7月13日 上午11:03

    已經解決了,抱歉,最近都沒有看信息。

    回復
  • 席大軍說道:2016年4月20日 上午11:50

    很好用的代碼,十分感謝!

    回復
  • John Hany說道:2016年4月20日 下午12:35

    多謝支持!

    回復
  • 元沖說道:2016年1月7日 下午4:05

    作者您好,我想做的是一幅圖像經過傅里葉變換之后,分別得到實部和虛部的數據,看了您的文章,深受啟發,但是遇到個問題,下面三步算是對原有圖像填充的尺寸,但是填充值后傅里葉變換得到的虛部和實部尺寸也已改變,怎么讓實部虛部數據都還原到填充前的尺寸呢?非常感謝!

    int opWidth = getOptimalDFTSize(srcImg.rows);

    int opHeight = getOptimalDFTSize(srcImg.cols);

    copyMakeBorder(srcImg, padded, 0, opWidth-srcImg.rows, 0, opHeight-srcImg.cols, BORDER_CONST

    回復
  • John Hany說道:2016年1月8日 上午12:43

    原有圖像的純色填充對頻域的影響應該是可以忽略的。況且這三個函數改變的是圖像的空間域,經過FFT之后得到的頻域圖像,其尺寸也失去了與原來空間域尺寸比較的意義。雖然OpenCV庫函數默認產生的頻譜圖像本身的大小與輸入圖像相同,但實際是對頻域橫縱坐標縮放的結果,其本質上仍然是沿坐標軸對稱的正方形區域。如果直接對頻譜圖像切割,會造成原始圖像頻率上信息的丟失(高頻或是低頻,取決于是否將四象限對調)。

    關于頻域對應空間域的關系,可以參考這個問題的第一個答案,個人覺得解釋得很精湛:http://dsp.stackexchange.com/questions/1637/what-does-frequency-domain-denote-in-case-of-images

    回復
  • 元沖說道:2016年1月10日 下午5:51

    感謝您的詳細解答,可能我太笨啦,有個問題還是想不太明白,希望您解惑。問題描述:其實我是在把MATLAB代碼變成c++時遇到的提取圖像經過fft變換后的實部和虛部數據這個問題,下面是MATLAB代碼:

    //tilted_array2,array1是灰度圖像

    //PCE是一個計算相關度的函數

    TA = fft2(tilted_array2); ? ? ? ? ? clear tilted_array2
    FA = fft2(array1); ? ? ? ? ? ? ? ? ?clear array1
    FF = FA .* TA; ? ? ? ? ? ? ? ? ? ? ?clear FA TA
    ret = real(ifft2(FF));

    detection = PCE(ret)

    可以看到ret得到的是FF傅里葉逆變換之后的實部數據,然后在PCE函數中得到相關度的數值。

    我把上面的MATLAB代碼變成如下c++代碼(調用了opencv庫)(菜鳥代碼可能寫的亂七八糟,見諒~):

    Mat paddedTA;
    ??int mTA = getOptimalDFTSize(tilted_array2.rows); ?
    ? int nTA = getOptimalDFTSize(tilted_array2.cols);
    ??copyMakeBorder(tilted_array2, paddedTA,0,mTA-tilted_array2.rows,0,nTA-tilted_array2.cols, BORDER_CONSTANT, Scalar::all(0));

    Mat planesTA[] ={Mat_<float(paddedTA),Mat::zeros(paddedTA.size(), CV_32F) };
    ?? ?Mat TA;
    ?? ?merge(planesTA, 2, TA);
    ?? ?dft(TA, TA);
    ?? ?split(TA,planesTA);?

    Mat paddedFA;
    ?? ?int mFA = getOptimalDFTSize(array1.rows);?
    ?? ?int nFA = getOptimalDFTSize(array1.cols);
    ?? ?copyMakeBorder(array1, paddedFA, 0, mFA-array1.rows, 0, nFA-array1.cols, BORDER_CONSTANT, Scalar::all(0));
    ?? ?Mat planesFA[] = {Mat_<float>(paddedFA), Mat::zeros(paddedFA.size(), CV_32F) };
    ?? ?Mat FA;
    ?? ?merge(planesFA, 2, FA);
    ?? ?dft(FA, FA);
    ?? ?split(FA, planesFA);

    ?//下面實現MATLAB中的FF = FA .* TA;? ?

    Mat FF1,FF2,FF3,FF4;
    ?? ?FF1=planesFA[0].mul(planesTA[0]);
    ?? ?FF2=planesFA[1].mul(planesTA[1]);
    ?? ?FF3=planesFA[0].mul(planesTA[1]);
    ?? ?FF4=planesFA[1].mul(planesTA[0]);

    ?? ?Mat planesFF[] = {Mat::zeros(paddedTA.size(), CV_32F), Mat::zeros(paddedTA.size(), CV_32F) };
    ?? ?subtract(FF1,FF2,planesFF[0]);
    ?? ?add(FF3,FF4,planesFF[1]);
    ?? ?Mat FF;
    ?? ?merge(planesFF, 2, FF);
    ?? ?
    ?? ?idft(FF,FF,cv::DFT_SCALE | cv::DFT_REAL_OUTPUT );
    ?? ?split(FF,planesFF);

    ?? ?Mat rel=planesFF[0];
    ? ??PCE(rel,detection);

    因為getOptimalDFTSize()函數是把輸入圖像的尺寸調節成2^n或2,3,5乘積這種形式,當我的輸入圖像像素滿足是2^n或2,3,5乘積這種形式時,程序不會對原圖像填充補0,這時opencv得到的planesTA、planesFA的實部和虛部數據都與MATLAB中TA、FA值一樣(行列數也都一樣),PCE函數得到的最后結果detection也是一樣的。但是,如果輸入圖像不滿足像素是2^n或2,3,5乘積這種形式時,程序會對輸入圖像進行填充,這樣得到的

    planesTA、planesFA的行列都是填充之后的行列,與輸入圖像行列不同,這時

    planesTA、planesFA的實部和虛部數據都與MATLAB中TA、FA對應數據肯定不同,但我想著可能對最后的PCE函數結果沒有影響,結果卻是這種情況下opencv的PCE結果與MATLAB也是不同的。

    所以我想請教您的是,輸入圖像像素不滿足條件時最后PCE結果不同的這個問題該怎么解決?不勝感謝!

    回復
  • 元沖說道:2016年1月10日 下午6:01

    為方便您的理解我貼出函數PCE的MATLAB代碼:

    shift_range = [0,0];

    Cinrange = rel(end-shift_range(1):end,end-shift_range(2):end);
    [max_cc, imax] = max(Cinrange(:));
    [ypeak, xpeak] = ind2sub(size(Cinrange),imax(1));
    peakheight = Cinrange(ypeak,xpeak);

    C_without_peak = RemoveNeighborhood(C,[ypeak, xpeak],squaresize);//RemoveNeighborhood是一個函數,下面有代碼
    correl = rel(end,end); ? ? ? ?clear rel
    PCE_energy = mean(C_without_peak.*C_without_peak);
    detection = peakheight.^2/PCE_energy * sign(peakheight);

    ?

    //RemoveNeighborhood函數

    function Y = RemoveNeighborhood(X,x,ssize)
    % Remove a 2-D neighborhood around x=[x1,x2] from matrix X and output a 1-D vector Y
    % ssize ? ? square neighborhood has size (ssize x ssize) square

    [M,N] = size(X);
    radius = (ssize-1)/2;
    X = circshift(X,[radius-x(1)+1,radius-x(2)+1]);
    Y = X(ssize+1:end,1:ssize); ?
    Y = Y(:);
    Y = [Y;X(M*ssize+1:end)'];

    非常感謝!

    ?

    ?

    ?

    回復
  • xtkwfn說道:2016年4月13日 下午2:53

    在fft前不進行速度優化,直接對圖像進行操作即可

  • Jason說道:2015年12月11日 上午10:55

    是把高頻部分移到中心位置吧?

    回復
  • John Hany說道:2015年12月11日 下午4:30

    象限換位的目的就是把頻域原點移到圖像中心,所以移位后低頻是位于中心區域的。頻譜圖像中心的小亮點F(0,0)表示圖像的平均灰度,也能說明這一點。

    感興趣的話,可以試著把一幅圖像作高斯模糊,再比較一下前后的頻譜圖像,坐標軸附近的點亮度是會增加的。

    回復
  • wahaha893611361說道:2015年11月9日 下午3:58

    試了一下 英文很好用,中文就不行了,葡萄牙語也不行,西班牙語還能湊合著用。總之,非常感謝博主分享。另有什么改善的方法么?

    回復
  • John Hany說道:2015年11月10日 下午12:37

    多謝支持!就方法上的改進可以用調整參數,過濾散點噪聲等手段,但從理論上我目前還沒有更好的改進思路。

    回復
  • ET民工說道:2015年9月12日 下午4:26

    非常感謝如此詳細的文章!

    回復
  • John Hany說道:2015年9月13日 上午1:25

    多謝支持!

    回復
  • 馬東興說道:2014年9月30日 下午7:01

    能不能判斷出一個圖片中的三角形傾斜的角度?

    回復
  • John Hany說道:2015年1月27日 下午5:38

    抱歉回復的很晚。本文的方法是不能用來計算單個三角形傾斜的角度的,只能考慮其他方法

    回復
  • 總結

    以上是生活随笔為你收集整理的OpenCV实现基于傅里叶变换(FFT)的旋转文本校正(文字方向检测)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。