http://blog.csdn.net/baixiaozhe/article/details/51762086

摄像头观察一个矩形的图片时往往只能得到一个扭曲的图片:

原图:

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

使用OpenCV的透视变换把图片矫正为正视的角度,大概过程:

1、通过灰度、模糊和二值化得到:

2、然后对查找图片外包矩形轮廓,并查找角点得到:

3、通过梯形四个角点和外包矩形的四个顶点得到变换矩阵,进行投射变换,最后得到:

如果图片看不到,请来 http://blog.csdn.NET/baixiaozhe/article/details/51762086

代码如下:

  1. //图片投射变换
  2. -(void)wrapImg{
  3. UIImage *imageInView  = [UIImage imageNamed:@"wrap"];
  4. Mat wrapSrc;
  5. //默认转为4通道 所以下面Scalar也得是4通道,否则不能正确实现颜色
  6. UIImageToMat(imageInView, wrapSrc);
  7. 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());
  8. //灰度
  9. Mat graymat;
  10. cvtColor(wrapSrc ,graymat,COLOR_BGR2GRAY);
  11. blur(graymat, graymat, Size2d(7,7));
  12. //二值化,灰度大于14的为白色 需要多调整 直至出现白色大梯形
  13. graymat=graymat>14;
  14. //Shi-Tomasi 角点算法参数
  15. int maxCorners=4;
  16. vector<Point2f> corners;
  17. double qualityLevel=0.01;
  18. double minDistance=100;//角点之间最小距离
  19. int blockSize=7;//轮廓越明显,取值越大
  20. bool useHarrisDetector=false;
  21. double k=0.04;
  22. //Shi-Tomasi 角点检测
  23. goodFeaturesToTrack(graymat,corners,maxCorners,qualityLevel,minDistance,Mat(),blockSize,useHarrisDetector,k);
  24. //cout<<"检测到角点数:"<<corners.size()<<endl;
  25. NSLog(@"检测到角点数:%lu",corners.size());
  26. int r=10;
  27. RNG rng;
  28. //画出来看看 找到的是不是四个顶点 另外角点检测出来的点顺序每次不一定相同
  29. /*
  30. if(corners.size()==4){
  31. circle(wrapSrc,corners[0],r,Scalar(255,0,0,255),2,8,0);//红
  32. circle(wrapSrc,corners[1],r,Scalar(0,255,0,255),2,8,0);//绿
  33. circle(wrapSrc,corners[2],r,Scalar(0,0,255,255),2,8,0);//蓝
  34. circle(wrapSrc,corners[3],r,Scalar(255,255,0,255),2,8,0);//黄
  35. }
  36. _imageView.image= MatToUIImage(wrapSrc) ;
  37. return;
  38. */
  39. std::vector<std::vector<cv::Point>> contoursOutLine;
  40. findContours(graymat,contoursOutLine,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);
  41. // 对轮廓计算其凸包//
  42. // 边界框
  43. cv::Rect boudRect;
  44. vector<Point2i>  poly ;
  45. for( int i = 0; i < contoursOutLine.size();  i++)
  46. {
  47. // 边界框
  48. boudRect=  boundingRect(contoursOutLine[i] );
  49. //面积过滤
  50. int tmpArea=boudRect.area();
  51. if(tmpArea>= 50000 )
  52. {
  53. rectangle(wrapSrc,cvPoint(boudRect.x,boudRect.y),cvPoint(boudRect.br().x ,boudRect.br().y ),Scalar(128),2);
  54. }
  55. }
  56. //src=wrapSrc(boudRect); 用这种方式截屏有时候会出错 不知咋回事
  57. //用IOS的 quartz api来截图
  58. UIImage *image=[UIImage imageWithCGImage:CGImageCreateWithImageInRect([imageInView CGImage], CGRectMake(boudRect.x,boudRect.y,boudRect.width,boudRect.height))];
  59. Mat src,warp_dst;
  60. UIImageToMat(image, src);
  61. warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
  62. //从梯形srcTri[4] 变换成 外包矩形dstTri[4]
  63. Point2f srcTri[4];
  64. Point2f dstTri[4];
  65. Point2f aRect1=boudRect.tl();
  66. // 梯形四个顶点 顺序为 左上  右上  左下  右下
  67. Point2f srcTri0 = Point2f(corners[0].x-aRect1.x  ,corners[0].y-aRect1.y );
  68. Point2f srcTri1 = Point2f(corners[2].x-aRect1.x  ,corners[2].y-aRect1.y );
  69. Point2f srcTri2 = Point2f(corners[1].x-aRect1.x  , corners[1].y-aRect1.y );
  70. Point2f srcTri3 = Point2f(corners[3].x-aRect1.x  , corners[3].y-aRect1.y );
  71. //查找左上点 取出外包矩形的中点,然后把梯形四个顶点与中点进行大小比较,如x,y都小于中点的是左上,x大于中点,y小于中点 则为右上
  72. Point2f boudRectCenter=Point2f(src.cols/2,src.rows/2);
  73. if(srcTri0.x>boudRectCenter.x){
  74. if(srcTri0.y>boudRectCenter.y){//右下
  75. srcTri[3]=srcTri0;
  76. }else{//右上
  77. srcTri[1]=srcTri0;
  78. }
  79. }else{
  80. if(srcTri0.y>boudRectCenter.y){//左下
  81. srcTri[2]=srcTri0;
  82. }else{//左上
  83. srcTri[0]=srcTri0;
  84. }
  85. }
  86. if(srcTri1.x>boudRectCenter.x){
  87. if(srcTri1.y>boudRectCenter.y){//右下
  88. srcTri[3]=srcTri1;
  89. }else{//右上
  90. srcTri[1]=srcTri1;
  91. }
  92. }else{
  93. if(srcTri1.y>boudRectCenter.y){//左下
  94. srcTri[2]=srcTri1;
  95. }else{//左上
  96. srcTri[0]=srcTri1;
  97. }
  98. }
  99. if(srcTri2.x>boudRectCenter.x){
  100. if(srcTri2.y>boudRectCenter.y){//右下
  101. srcTri[3]=srcTri2;
  102. }else{//右上
  103. srcTri[1]=srcTri2;
  104. }
  105. }else{
  106. if(srcTri2.y>boudRectCenter.y){//左下
  107. srcTri[2]=srcTri2;
  108. }else{//左上
  109. srcTri[0]=srcTri2;
  110. }
  111. }
  112. if(srcTri3.x>boudRectCenter.x){
  113. if(srcTri3.y>boudRectCenter.y){//右下
  114. srcTri[3]=srcTri3;
  115. }else{//右上
  116. srcTri[1]=srcTri3;
  117. }
  118. }else{
  119. if(srcTri3.y>boudRectCenter.y){//左下
  120. srcTri[2]=srcTri3;
  121. }else{//左上
  122. srcTri[0]=srcTri3;
  123. }
  124. }
  125. // 画出来 看看顺序对不对
  126. circle(src,srcTri[0],r,Scalar(255,0,0,255),-1,8,0);//红 左上
  127. circle(src,srcTri[1],r,Scalar(0,255,0,255),-1,8,0);//绿 右上
  128. circle(src,srcTri[2],r,Scalar(0,0,255,255),-1,8,0);//蓝 左下
  129. circle(src,srcTri[3],r,Scalar(255,255,0,255),-1,8,0);//黄 右下
  130. _imageView.image= MatToUIImage(src) ;
  131. //  return;
  132. // 外包矩形的四个顶点, 顺序为 左上  右上  左下  右下
  133. dstTri[0] = Point2f( 0,0 );
  134. dstTri[1] = Point2f( src.cols - 1, 0 );
  135. dstTri[2] = Point2f( 0, src.rows - 1 );
  136. dstTri[3] = Point2f( src.cols - 1, src.rows - 1 );
  137. //自由变换 透视变换矩阵3*3
  138. Mat warp_matrix( 3, 3, CV_32FC1 );
  139. warp_matrix=getPerspectiveTransform(srcTri  ,dstTri  );
  140. warpPerspective( src, warp_dst, warp_matrix, warp_dst.size(),WARP_FILL_OUTLIERS);
  141. _imageView.image= MatToUIImage(warp_dst) ;
  142. }

_imageView是图片控件的插头

opencv-ios开发笔记9 使用透视变换矫正扭曲的图片的更多相关文章

  1. iOS开发笔记7:Text、UI交互细节、两个动画效果等

    Text主要总结UILabel.UITextField.UITextView.UIMenuController以及UIWebView/WKWebView相关的一些问题. UI细节主要总结界面交互开发中 ...

  2. iOS开发笔记-两种单例模式的写法

    iOS开发笔记-两种单例模式的写法   单例模式是开发中最常用的写法之一,iOS的单例模式有两种官方写法,如下: 不使用GCD #import "ServiceManager.h" ...

  3. iOS开发笔记--什么时候调用layoutSubviews

    iOS开发笔记--什么时候调用layoutSubviews 分类: iOS2014-04-22 16:15 610人阅读 评论(0) 收藏 举报 今天在写程序时候遇见layoutSubviews触发时 ...

  4. IOS开发笔记(4)数据离线缓存与读取

    IOS开发笔记(4)数据离线缓存与读取 分类: IOS学习2012-12-06 16:30 7082人阅读 评论(0) 收藏 举报 iosiOSIOS 方法一:一般将服务器第一次返回的数据保存在沙盒里 ...

  5. IOS开发笔记 IOS如何访问通讯录

    IOS开发笔记  IOS如何访问通讯录 其实我是反对这类的需求,你说你读我的隐私,我肯定不愿意的. 幸好ios6.0 以后给了个权限控制.当打开app的时候你可以选择拒绝. 实现方法: [plain] ...

  6. 【Swift】iOS开发笔记(二)

    前言 这个系列主要是一些开发中遇到的坑记录分享,有助于初学者跨过这些坑,攒够 7 条发一篇. 声明  欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯 ...

  7. 菜鸟手下的iOS开发笔记(swift)

    在阳春4月的一天晨会上,有一个老板和蔼的对他的一个菜鸟手下说:“你既然会Android,那你能不能开发iOS?” 不是说好的要外包的吗?内心跌宕,但是表面淡定的菜鸟手下弱弱的回道:“可以试试”. 第二 ...

  8. OpenCV iOS开发(一)——安装(转)

    OpenCV是一个开源跨平台的的计算机视觉和机器学习库,可以用来做图片视频的处理.图形识别.机器学习等应用.本文将介绍OpenCV iOS开发中的Hello World起步. 安装 OpenCV安装的 ...

  9. 李洪强iOS开发之 - 实现九宫格并使用SDWebImage下载图片

     李洪强iOS开发之 - 实现九宫格并使用SDWebImage下载图片  源码:  // //  ViewController.m //  08-九宫格扩展 // //  Created by 李洪强 ...

随机推荐

  1. MQTT-SN协议乱翻之小结篇

    前言 这里简单做一些小结和对比,针对前面的协议翻译部分,一阶段的学习完结. MQTT-SN VS MQTT MQTT-SN基于MQTT原有语义,但做了很多的调整.比如: 一个CONNECT消息被拆分为 ...

  2. css3-巧用选择器 “:target”

    今天(昨天)又发现一个知识盲区 css3的:target标签,之前学习的时候就是一眼扫过,说是认识了,但其实也就记了三分钟,合上书就全忘光了. 直到昨天,遇到一个有意思的题目,用css3新特性做一个类 ...

  3. (分解质因数模板)求 1~r 内与 n 互素的元素个数

    void Solve(LL n){ ///分解质因数保存结果于p p.clear(); ; i*i<=n; i++) ){ p.push_back(i); ) n/=i; } ) p.push_ ...

  4. 根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换

    转:http://blog.csdn.net/q445697127/article/details/40537945 /** * 根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换 ...

  5. Unity3D笔记九 发送广播与消息、利用脚本控制游戏

    一.发送广播与消息 游戏对象之间发送的广播与消息分为三种:第一种向子对象发送,将发送至该对象的同辈对象或者子孙对象中:第二种为给自己发送,发送至自己本身对象:第三种为向父对象发送,发送至该对象的同辈或 ...

  6. 系统事件管理(Events) ---- HTML5+

    模块:events Events模块管理客户端事件,包括系统事件,如扩展API加载完毕.程序前后台切换等. 比如说:网络的链接的和断开这种事件,系统从前台走到后台这种事件: 不包括:点击和滑动页面事件 ...

  7. [Gradle] 发布构件到本地仓库

    配置 需要发布构件的模块 build.gradle 加入如下配置 apply plugin: 'maven-publish' publishing { publications { mavenJava ...

  8. 【MySQL】为什么不要问我DB极限QPS/TPS

    为什么不要问我DB极限QPS/TPS 背景 相信很多开发都会有这个疑问,DB到底可以支撑多大的业务量,如何去评估?对于这个很专业的问题,DBA也没有办法直接告诉你,更多的都是靠经验提供一个看似靠谱的结 ...

  9. Cisco配置发送日志到日志服务器

    Cisco配置发送日志到日志服务器logging 172.16.6.22logging onlogging trap 7  //指定日志消息的级别 (0:紧急(Emergencies) 1:告警(Al ...

  10. 徐州网络赛J-Maze Designer【最小生成树】【LCA】

    After the long vacation, the maze designer master has to do his job. A tour company gives him a map ...