http://blog.csdn.net/baixiaozhe/article/details/51762086
攝像頭觀察一個矩形的圖片時往往只能得到一個扭曲的圖片:
原圖:

實際情況是攝像頭經常從某個角度觀察圖片:
使用OpenCV的透視變換把圖片矯正為正視的角度,大概過程:
1、通過灰度、模糊和二值化得到:

2、然后對查找圖片外包矩形輪廓,並查找角點得到:

3、通過梯形四個角點和外包矩形的四個頂點得到變換矩陣,進行投射變換,最后得到:

如果圖片看不到,請來 http://blog.csdn.NET/baixiaozhe/article/details/51762086
代碼如下:
- //圖片投射變換
- -(void)wrapImg{
- UIImage *imageInView = [UIImage imageNamed:@"wrap"];
- Mat wrapSrc;
- //默認轉為4通道 所以下面Scalar也得是4通道,否則不能正確實現顏色
- UIImageToMat(imageInView, wrapSrc);
- NSLog(@"wrapSrc cols%d rows%d channels%d type%d depth%d elemSize%zu",wrapSrc.cols,wrapSrc.rows,wrapSrc.channels(),wrapSrc.type(),wrapSrc.depth(),wrapSrc.elemSize());
- //灰度
- Mat graymat;
- cvtColor(wrapSrc ,graymat,COLOR_BGR2GRAY);
- blur(graymat, graymat, Size2d(7,7));
- //二值化,灰度大於14的為白色 需要多調整 直至出現白色大梯形
- graymat=graymat>14;
- //Shi-Tomasi 角點算法參數
- int maxCorners=4;
- vector<Point2f> corners;
- double qualityLevel=0.01;
- double minDistance=100;//角點之間最小距離
- int blockSize=7;//輪廓越明顯,取值越大
- bool useHarrisDetector=false;
- double k=0.04;
- //Shi-Tomasi 角點檢測
- goodFeaturesToTrack(graymat,corners,maxCorners,qualityLevel,minDistance,Mat(),blockSize,useHarrisDetector,k);
- //cout<<"檢測到角點數:"<<corners.size()<<endl;
- NSLog(@"檢測到角點數:%lu",corners.size());
- int r=10;
- RNG rng;
- //畫出來看看 找到的是不是四個頂點 另外角點檢測出來的點順序每次不一定相同
- /*
- if(corners.size()==4){
- circle(wrapSrc,corners[0],r,Scalar(255,0,0,255),2,8,0);//紅
- circle(wrapSrc,corners[1],r,Scalar(0,255,0,255),2,8,0);//綠
- circle(wrapSrc,corners[2],r,Scalar(0,0,255,255),2,8,0);//藍
- circle(wrapSrc,corners[3],r,Scalar(255,255,0,255),2,8,0);//黃
- }
- _imageView.image= MatToUIImage(wrapSrc) ;
- return;
- */
- std::vector<std::vector<cv::Point>> contoursOutLine;
- findContours(graymat,contoursOutLine,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);
- // 對輪廓計算其凸包//
- // 邊界框
- cv::Rect boudRect;
- vector<Point2i> poly ;
- for( int i = 0; i < contoursOutLine.size(); i++)
- {
- // 邊界框
- boudRect= boundingRect(contoursOutLine[i] );
- //面積過濾
- int tmpArea=boudRect.area();
- if(tmpArea>= 50000 )
- {
- rectangle(wrapSrc,cvPoint(boudRect.x,boudRect.y),cvPoint(boudRect.br().x ,boudRect.br().y ),Scalar(128),2);
- }
- }
- //src=wrapSrc(boudRect); 用這種方式截屏有時候會出錯 不知咋回事
- //用IOS的 quartz api來截圖
- UIImage *image=[UIImage imageWithCGImage:CGImageCreateWithImageInRect([imageInView CGImage], CGRectMake(boudRect.x,boudRect.y,boudRect.width,boudRect.height))];
- Mat src,warp_dst;
- UIImageToMat(image, src);
- warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
- //從梯形srcTri[4] 變換成 外包矩形dstTri[4]
- Point2f srcTri[4];
- Point2f dstTri[4];
- Point2f aRect1=boudRect.tl();
- // 梯形四個頂點 順序為 左上 右上 左下 右下
- Point2f srcTri0 = Point2f(corners[0].x-aRect1.x ,corners[0].y-aRect1.y );
- Point2f srcTri1 = Point2f(corners[2].x-aRect1.x ,corners[2].y-aRect1.y );
- Point2f srcTri2 = Point2f(corners[1].x-aRect1.x , corners[1].y-aRect1.y );
- Point2f srcTri3 = Point2f(corners[3].x-aRect1.x , corners[3].y-aRect1.y );
- //查找左上點 取出外包矩形的中點,然后把梯形四個頂點與中點進行大小比較,如x,y都小於中點的是左上,x大於中點,y小於中點 則為右上
- Point2f boudRectCenter=Point2f(src.cols/2,src.rows/2);
- if(srcTri0.x>boudRectCenter.x){
- if(srcTri0.y>boudRectCenter.y){//右下
- srcTri[3]=srcTri0;
- }else{//右上
- srcTri[1]=srcTri0;
- }
- }else{
- if(srcTri0.y>boudRectCenter.y){//左下
- srcTri[2]=srcTri0;
- }else{//左上
- srcTri[0]=srcTri0;
- }
- }
- if(srcTri1.x>boudRectCenter.x){
- if(srcTri1.y>boudRectCenter.y){//右下
- srcTri[3]=srcTri1;
- }else{//右上
- srcTri[1]=srcTri1;
- }
- }else{
- if(srcTri1.y>boudRectCenter.y){//左下
- srcTri[2]=srcTri1;
- }else{//左上
- srcTri[0]=srcTri1;
- }
- }
- if(srcTri2.x>boudRectCenter.x){
- if(srcTri2.y>boudRectCenter.y){//右下
- srcTri[3]=srcTri2;
- }else{//右上
- srcTri[1]=srcTri2;
- }
- }else{
- if(srcTri2.y>boudRectCenter.y){//左下
- srcTri[2]=srcTri2;
- }else{//左上
- srcTri[0]=srcTri2;
- }
- }
- if(srcTri3.x>boudRectCenter.x){
- if(srcTri3.y>boudRectCenter.y){//右下
- srcTri[3]=srcTri3;
- }else{//右上
- srcTri[1]=srcTri3;
- }
- }else{
- if(srcTri3.y>boudRectCenter.y){//左下
- srcTri[2]=srcTri3;
- }else{//左上
- srcTri[0]=srcTri3;
- }
- }
- // 畫出來 看看順序對不對
- circle(src,srcTri[0],r,Scalar(255,0,0,255),-1,8,0);//紅 左上
- circle(src,srcTri[1],r,Scalar(0,255,0,255),-1,8,0);//綠 右上
- circle(src,srcTri[2],r,Scalar(0,0,255,255),-1,8,0);//藍 左下
- circle(src,srcTri[3],r,Scalar(255,255,0,255),-1,8,0);//黃 右下
- _imageView.image= MatToUIImage(src) ;
- // return;
- // 外包矩形的四個頂點, 順序為 左上 右上 左下 右下
- dstTri[0] = Point2f( 0,0 );
- dstTri[1] = Point2f( src.cols - 1, 0 );
- dstTri[2] = Point2f( 0, src.rows - 1 );
- dstTri[3] = Point2f( src.cols - 1, src.rows - 1 );
- //自由變換 透視變換矩陣3*3
- Mat warp_matrix( 3, 3, CV_32FC1 );
- warp_matrix=getPerspectiveTransform(srcTri ,dstTri );
- warpPerspective( src, warp_dst, warp_matrix, warp_dst.size(),WARP_FILL_OUTLIERS);
- _imageView.image= MatToUIImage(warp_dst) ;
- }
_imageView是圖片控件的插頭
