前一篇画出了最小外接矩形,但是有时候画出来的矩形由于中间像素干扰或者是其他原因矩形框并不是真正想要的

如图1是一个信号的雨图,被矩形框分割成了多个小框:

需要合并矩形框达到的效果:

主要思想:

扫描两次最小外接矩形,第一次扫描出的矩形是图一的小矩形,遍历vector指定一个合并最大距离(假设是80),达到指定距离使用画矩形函数将这两个矩形占据的组合区域染成实心矩形。

第二次扫描直接扫描之前画的实心矩形图确定最终边框

过程图 膨胀处理和像素翻转:

代码:

#include "core/core.hpp"
#include "highgui/highgui.hpp"
#include "imgproc/imgproc.hpp"
#include "iostream"
#include "cmath"
using namespace std;
using namespace cv;int Distance(Rect rect1,Rect rect2)
{
// 用于判断rect1在rect2的第三象限里 用于反转X轴用
bool isInversion;
// 保存两个比较的点
Point point1;
Point point2;
// 判断 rect1 在rect2的上面还是下面 也就是说是在第一、二象限还是在三四象限
if(rect1.y<rect2.y)
{
// 判断rect1 在rect2的左边还是右边 也就是说是在 一象限还是二象限
isInversion= rect1.x<rect2.x;
if(isInversion )
{
// 取rect1的右上点
point1 = Point(rect1.x+rect1.width,rect1.y+rect1.height);
// 取rect2的左下点
point2 = Point(rect2.x,rect2.y);
}else
{
// 取rect1的左上点
point1 = Point(rect1.x,rect1.y+rect1.height);
// 取rect2的右下点
point2 = Point(rect2.x+rect2.width,rect2.y);
}
}else
{
// 判断rect1 在rect2的左边还是右边 也就是说是在 三象限还是四象限
isInversion = rect1.x>rect2.x;
if(isInversion)
{
// 取rect2的右上点
point1 = Point(rect2.x+rect2.width,rect2.y+rect2.height);
// 取rect1的左下点
point2 = Point(rect1.x,rect1.y);
}else
{
// 取rect2的左上点
point1 = Point(rect2.x,rect2.y+rect2.height);
// 取rect1的右下点
point2 = Point(rect1.x+rect1.width,rect1.y);
}
}
// 做向量减法
Point dPoint = point2 -point1;
// 如果反转x轴
dPoint.x = isInversion? dPoint.x:-dPoint.x;
// 如果这个向量在第三象限里 那么这两个矩形相交 返回-1
if(dPoint.x<&& dPoint.y<)
return -;
// 如果x<0 返回y
if(dPoint.x<)
return dPoint.y;
// 如果y小于0 返回x
if(dPoint.y<)
return dPoint.x;
// 返回这个向量的长度
return ;
} Mat change(Mat src)
{
int cPointR,cPointG,cPointB,cPoint;
for(int i=; i<src.rows; i++)
{
for(int j=; j<src.cols; j++)
{
cPointB=src.at<Vec3b>(i,j)[]=src.at<Vec3b>(i,j)[];
cPointG=src.at<Vec3b>(i,j)[]=src.at<Vec3b>(i,j)[];
cPointR=src.at<Vec3b>(i,j)[]=src.at<Vec3b>(i,j)[];
if(cPointR>||cPointG>||cPointB>)
{
src.at<Vec3b>(i,j)[]=;
src.at<Vec3b>(i,j)[]=;
src.at<Vec3b>(i,j)[]=;
}
else
{
src.at<Vec3b>(i,j)[]=;
src.at<Vec3b>(i,j)[]=;
src.at<Vec3b>(i,j)[]=;
}
cPointB=src.at<Vec3b>(i,j)[]=src.at<Vec3b>(i,j)[];
cPointG=src.at<Vec3b>(i,j)[]=src.at<Vec3b>(i,j)[];
cPointR=src.at<Vec3b>(i,j)[]=src.at<Vec3b>(i,j)[];
//cout<<"("<<cPointB<<","<<cPointG<<","<<cPointR<<")"<<" ";
}
//cout<<endl;
}
return src;
}
//imageOut 原图 ; dilation_dst膨胀图 ;imageSource单通道灰度图 ; imageTemp白图
int main(int argc,char *argv[])
{ //freopen("stdout.txt","w",stdout);
///读图 读入一个3通道彩图
Mat imageOut=imread(argv[],); //读入一个3通道彩图
Mat grayImage;cvtColor(imageOut,grayImage,CV_BGR2GRAY); //存一个灰度图
//imshow("3通道彩图",imageOut);
Mat imageTemp=imread("C:\\Users\\Administrator\\Desktop\\Rec\\temp.png",); //读一个大小一样的白图
///腐蚀去噪处理
Mat erosion_dst,temp;
int erosion_size=;
Mat element = getStructuringElement( MORPH_RECT,Size( *erosion_size + , *erosion_size+ ),
Point( erosion_size, erosion_size ) ); //腐蚀去噪处理参数
erode( imageOut,erosion_dst , element );//腐蚀去噪处理
//imshow( "腐蚀去噪处理", erosion_dst );
imwrite("C:\\Users\\Administrator\\Desktop\\Rec\\erosion_dst.png", erosion_dst);
///像素变换
Mat change_dst=change(erosion_dst);
//imshow( "像素变换", change_dst );
imwrite("C:\\Users\\Administrator\\Desktop\\Rec\\change_dst.png", change_dst);
///单通道灰度图
Mat imageSource;
cvtColor(change_dst,imageSource,CV_BGR2GRAY); //转换为单通道灰度图
//imshow("单通道灰度图",imageSource); ///接下来对imageSource单通道灰度图做处理
Mat image;
blur(imageSource,image,Size(,));
threshold(image,image,,,CV_THRESH_OTSU);
//imshow("image",image);
//imwrite("C:\\Users\\Administrator\\Desktop\\Rec\\myimage.png", image); ///寻找最外层轮廓
vector<vector<Point> > contours0;
vector<Vec4i> hierarchy0;
findContours(image,contours0,hierarchy0,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point());
//imshow("最外层轮廓",image); ///连接矩形区域///////////////////////////////////////////////////////////////////
//Mat RECtest=imread("C:\\Users\\Administrator\\Desktop\\Rec\\temp.png",0);
cout<<contours0.size()<<endl;
for(int i=; i<contours0.size()-; i++)
{
RotatedRect rect_i=minAreaRect(contours0[i]);
Point2f P_i[];
rect_i.points(P_i);
rectangle(image,Point(P_i[].x,P_i[].y),Point(P_i[].x,P_i[].y),Scalar(,,),-,);
for(int j=i+; j<contours0.size(); j++)
{
RotatedRect rect_j=minAreaRect(contours0[j]);
Point2f P_j[];
rect_j.points(P_j);
double recArea_i=contourArea(contours0[i]);
double recArea_j=contourArea(contours0[j]);
//cout<<(P_i[3].x-P_i[2].x)*(P_i[1].y-P_i[2].y)<<" -> "<<(P_j[3].x-P_j[2].x)*(P_j[1].y-P_j[2].y)<<" ";
Rect r_j = rect_j.boundingRect();
Rect r_i = rect_i.boundingRect();
//cout<<recArea_i<<" -> "<<recArea_j<<" "<<Distance(r_i,r_j)<<" ";
if(Distance(r_i,r_j)<=)
{
int minx=min(P_i[].x,P_j[].x);
int maxx=max(P_i[].x,P_j[].x);
int miny=min(P_i[].y,P_j[].y);
int maxy=max(P_i[].y,P_j[].y);
rectangle(image,Point(minx,miny),Point(maxx,maxy),Scalar(,,),-,);//画实心矩形
//rectangle(RECtest,Point(minx,miny),Point(maxx,maxy),Scalar(0,0,0),-1,1);
//cout<<minx<<","<<miny<<" "<<maxx<<","<<maxy<<endl;
//line(image,P_i[2],P_j[0],Scalar(0,0,0),1); //画线
//line(RECtest,P_i[2],P_j[0],Scalar(0,0,0),1);
//cout<<"yes";
}
//cout<<endl;
/*rect_i=rect_j;
for(int k=0;k<=3;k++)
{
P_i[k].x=P_j[k].x;
P_i[k].y=P_j[k].y;
}*/
}
//cout<<"---------------------------------------------------"<<endl;
}
//imwrite("C:\\Users\\Administrator\\Desktop\\Rec\\RECtest.png", RECtest);
///////////////////////////////////////////////////////////////////
/*
*/
///////////////////////////////////////////////////////////////////
//imshow("实心矩形",image);
//imwrite("C:\\Users\\Administrator\\Desktop\\Rec\\images.png", image);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(image,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point());
Mat imageContours=Mat::zeros(image.size(),CV_8UC1); //最小外接矩形画布 for(int i=; i<contours.size(); i++)
{
///绘制轮廓
drawContours(imageContours,contours,i,Scalar(,,),,,hierarchy); ///绘制轮廓的最小外结矩形
RotatedRect rect=minAreaRect(contours[i]);
Point2f P[];
rect.points(P); int minx=min(P[].x,P[].x);
int maxx=max(P[].x,P[].x);
int miny=min(P[].y,P[].y);
int maxy=max(P[].y,P[].y);
rectangle(grayImage,Point(minx,miny),Point(maxx,maxy),Scalar(,,),,);//二值图绘线
rectangle(imageOut,Point(minx,miny),Point(maxx,maxy),Scalar(,,),,);//原图绘线
rectangle(imageTemp,Point(minx,miny),Point(maxx,maxy),Scalar(,,),,);//白图 }
imwrite("C:\\Users\\Administrator\\Desktop\\Rec\\new1.png", grayImage);
imwrite("C:\\Users\\Administrator\\Desktop\\Rec\\new2.png", imageOut);
imwrite("C:\\Users\\Administrator\\Desktop\\Rec\\new3.png", imageTemp);
waitKey();
return ;
}

Opencv 最小外接矩形合并拼接的更多相关文章

  1. Opencv 改进的外接矩形合并拼接方法

    上一篇中的方法存在的问题是矩形框不够精确,而且效果不能达到要求 这里使用凸包检测的方法,并将原来膨胀系数由20缩小到5,达到了更好的效果 效果图: 效果图: 代码: #include <open ...

  2. opencv学习之路(26)、轮廓查找与绘制(五)——最小外接矩形

    一.简介 二.轮廓最小外接矩形的绘制 #include "opencv2/opencv.hpp" using namespace cv; void main() { //轮廓最小外 ...

  3. Opencv 图片边缘检测和最小外接矩形

    #include "core/core.hpp" #include "highgui/highgui.hpp" #include "imgproc/i ...

  4. Opencv绘制最小外接矩形、最小外接圆

    Opencv中求点集的最小外结矩使用方法minAreaRect,求点集的最小外接圆使用方法minEnclosingCircle. minAreaRect方法原型: RotatedRect minAre ...

  5. cv2.minAreaRect() 生成最小外接矩形

    简介   使用python opencv返回点集cnt的最小外接矩形,所用函数为 cv2.minAreaRect(cnt) ,cnt是所要求最小外接矩形的点集数组或向量,这个点集不定个数.   cv2 ...

  6. opencv轮廓外接矩形

    1.寻找轮廓 api void cv::findContours( InputOutputArray image, OutputArrayOfArrays contours, OutputArray ...

  7. BZOJ 1185: [HNOI2007]最小矩形覆盖-旋转卡壳法求点集最小外接矩形(面积)并输出四个顶点坐标-备忘板子

    来源:旋转卡壳法求点集最小外接矩形(面积)并输出四个顶点坐标 BZOJ又崩了,直接贴一下人家的代码. 代码: #include"stdio.h" #include"str ...

  8. OpenCV 求外接矩形以及旋转角度

    程序没有写完整,大概功能就是实现了,希望大家分享学习,把他改对 // FindRotation-angle.cpp : 定义控制台应用程序的入口点. // // findContours.cpp : ...

  9. opencv学习之路(25)、轮廓查找与绘制(四)——正外接矩形

    一.简介 二.外接矩形的查找绘制 #include "opencv2/opencv.hpp" using namespace cv; void main() { //外接矩形的查找 ...

随机推荐

  1. 命令行下修改postgres密码

    1. 修改PostgreSQL数据库默认用户postgres的密码 PostgreSQL数据库创建一个postgres用户作为数据库的管理员,密码随机,所以需要修改密码,方式如下: 步骤一:登录Pos ...

  2. 触屏版轻量级分页插件jqPagination分享

    说到HTML5和jquery上的分页问题,优秀的分页插件网上一抓一大把,然而同时适合兼容在Ipad和手机端的网站分页却不是特别多. 或许有人会说,触屏现在流行下拉底部后加载下一页内容,类似微博和QQ空 ...

  3. cinema 4d 包括宝典 --- 改线 循环边 建模布线原则

    cinema 4d 一.视图控制与物体控制 1.摇移      alt+鼠标左键   转圈看物体  改变角度 2.平移  alt +鼠标中键      不改变角度  移动 3.推拉  alt+鼠标右键 ...

  4. concurrent.futures模块(进程池&线程池)

    1.线程池的概念 由于python中的GIL导致每个进程一次只能运行一个线程,在I/O密集型的操作中可以开启多线程,但是在使用多线程处理任务时候,不是线程越多越好,因为在线程切换的时候,需要切换上下文 ...

  5. TOJ4537: n阶行列式

    4537: n阶行列式  Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByteTotal Submit: 28       ...

  6. PDO 使用prepared statement 预处理LIMIT等非表字段参数

    由于一些驱动不支持原生的预处理语句,因此PDO可以完全模拟预处理.PDO的模拟预处理是默认打开的,即便MYSQL驱动本身支持预处理,在默认打开的状态下,PDO是不会用到MYSQL本身提供的预处理功能. ...

  7. HLG2179 组合(dfs水水更健康)

    组合 Time Limit: 1000 MS Memory Limit: 32768 K Total Submit: 57(38 users) Total Accepted: 43(36 users) ...

  8. Codeforces834D - The Bakery

    Portal Description 给出一个\(n(n\leq35000)\)个数的数列\(\{a_i\}\)和\(m(m\leq50)\).将原数列划分成\(m\)个连续的部分,每个部分的权值等于 ...

  9. 面试题之redis的内存回收策略

    1.maxmemory-policy noeviction(默认):内存空间不足会报错 2.allkeys-lru:最少使用的数据去淘汰 3.allkeys-random:随机淘汰一些key 4.vo ...

  10. bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check

    [Wc2010]重建计划 Time Limit: 40 Sec  Memory Limit: 162 MBSubmit: 4345  Solved: 1054[Submit][Status][Disc ...