您的位置:首页 > 移动开发 > IOS开发

opencv-ios开发笔记9 使用透视变换矫正扭曲的图片

2016-06-26 12:08 866 查看
摄像头观察一个矩形的图片时往往只能得到一个扭曲的图片:

原图:



实际情况是摄像头经常从某个角度观察图片:


 

使用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是图片控件的插头
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息