【学习opencv】opencv : 边缘区域去除 VS 边界扩展copyMakeBorder

边缘区域的定义:图像上的一个区域位于边缘。如下图所示:

 

标号1 为一个边缘区域

 现在希望可以将图像中的边缘区域去除。一个简单的思路如下:

           遍历图像上下左右四条边界上的像素。设置一个计数器和一个最小边界阈值。当边界满足要求的像素个数大于阈值,就作为一个待处理区域。并选取其中一个坐标点作为种子点进行满水填充为0。

代码如下:

// 边缘区域去除1.cpp : 定义控制台应用程序的入口点。
// VS2010 opencv2.4.9

#include "stdafx.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat src=imread("1.jpg",1);; 
	imshow("src",src);

	//灰度化
	Mat grayImage;
	cvtColor(src,grayImage,CV_BGR2GRAY);

	//二值化原图像
	Mat thresImage=Mat::zeros(grayImage.rows,grayImage.cols,CV_8UC1);
	threshold(grayImage,thresImage,250,255,THRESH_BINARY);
	//imshow("thresImage",thresImage);


	方法1:轮廓提取与阈值填充
	//vector<vector<Point>> contours;  
	//vector<Point>contours_sum;
	 find  
	//findContours(thresImage,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE); 
	//for(int n =0; n<contours.size();++n)
	//{
	//	cout<<contours[n].size()<<endl;
	//	for (int i=0;i<contours[n].size();++i)
	//	{
	//		contours_sum.push_back(contours[n][i]);
	//	}
	//}
	cout<<contours_sum.size()<<endl;
	//for (int i=0;i<contours_sum.size();++i)
	//{
	//	//cout<<contours_sum[i]<<endl;
	//	floodFill(thresImage,contours_sum[i],0);//漫水填充法
	//}
	//imshow("floodFill",thresImage);




	const int nr=thresImage.rows;
	const int nc=thresImage.cols;
	Mat edge[4];
	edge[0] = thresImage.row(0);    //up
	edge[1] = thresImage.row(nr-1); //bottom
	edge[2] = thresImage.col(0);    //left
	edge[3] = thresImage.col(nc-1); //right

	std::vector<Point> edgePts;
	const int minLength=std::min(nr,nc)/4;
	for(int i=0;i<4;++i)
	{
		std::vector<Point> line;
		Mat_<uchar>::const_iterator iter = edge[i].begin<uchar>();       //当前像素
		Mat_<uchar>::const_iterator nextIter = edge[i].begin<uchar>()+1; //下一个像素
		while(nextIter!=edge[i].end<uchar>())
		{
			if(*iter==255)
			{
				if(*nextIter==255)
				{
					Point pt = iter.pos();
					if(i==1)
						pt.y = nr-1;
					if(i==3)
						pt.x = nc-1;
					
					//line.push_back(pt);
					edgePts.push_back(pt);
				}
				//if(*nextIter!=255)
				//{
				//	if(line.size()>minLength)
				//		edgePts.push_back(line.at(line.size()/2));
				//	line.clear();
				//}
			}
			++iter;
			++nextIter;
		}
	}
	
	for(int n =0; n<edgePts.size();++n)
		//cout<<edgePts[n];
		floodFill(thresImage,edgePts[n],0);//漫水填充法
	imshow("floodFill",thresImage);
	waitKey( 0 );    
}





copyMakeBorder可以处理多通道也可以处理单通道

如何使用OpenCV函数 copyMakeBorder 设置边界(添加额外的边界)。


假设src为以下矩阵

我们首先只在一个方向上,讨论,例如 top方向:

top =5 或者 10, bottom =0,left 0,right =0;注意我们这里故意让top的值,大于 src的rows,即行数。查看结果

borderType = BORDER_REFLECT:反射


解释:当按BORDER_REFLECT,向上给src加边界时,是按照src的反射机制来加的。

borderType = BORDER_REPLICATE:复制


解释:当BORDER_REPLICATE时,代表只复制边界。

当left=5,bottom =5时,

BORDER_TYPE = BORDER_REFLECT_101:


解释:101,已经表明 0不参加反射机制。也即是以第一行为镜面,做反射

给图像添加边界

目标

本文档尝试解答如下问题:

  • 如何使用OpenCV函数 copyMakeBorder 设置边界(添加额外的边界)。

Theory

Note

 

以下内容来自于Bradski和Kaehler的大作 Learning OpenCV 。

  1. 前一节我们学习了图像的卷积操作。一个很自然的问题是如何处理卷积边缘。当卷积点在图像边界时会发生什么,如何处理这个问题?

  2. 大多数用到卷积操作的OpenCV函数都是将给定图像拷贝到另一个轻微变大的图像中,然后自动填充图像边界(通过下面示例代码中的各种方式)。这样卷积操作就可以在边界像素安全执行了(填充边界在操作完成后会自动删除)。

  3. 本文档将会探讨填充图像边界的两种方法:

    1. BORDER_CONSTANT: 使用常数填充边界 (i.e. 黑色或者 0)
    2. BORDER_REPLICATE: 复制原图中最临近的行或者列。

    源码部分给出更加详细的解释。

源码

  1. 本程序做什么?

    • 装载图像

    • 由用户决定使用哪种填充方式。有两个选项:

      1. 常数边界: 所有新增边界像素使用一个常数,程序每0.5秒会产生一个随机数更新该常数值。
      2. 复制边界: 复制原图像的边界像素。

      用户可以选择按 ‘c’ 键 (常数边界) 或者 ‘r’ 键 (复制边界)

    • 当用户按 ‘ESC’ 键,程序退出。

  2. 下面是本教程的源码, 你也可以从 这里 下载

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>

using namespace cv;

/// 全局变量
Mat src, dst;
int top, bottom, left, right;
int borderType;
Scalar value;
char* window_name = "copyMakeBorder Demo";
RNG rng(12345);

/** @函数 main  */
int main( int argc, char** argv )
{

  int c;

  /// 装载图像
  src = imread( argv[1] );

  if( !src.data )
  { return -1;
    printf(" No data entered, please enter the path to an image file \n");
  }

  /// 使用说明
  printf( "\n \t copyMakeBorder Demo: \n" );
  printf( "\t -------------------- \n" );
  printf( " ** Press 'c' to set the border to a random constant value \n");
  printf( " ** Press 'r' to set the border to be replicated \n");
  printf( " ** Press 'ESC' to exit the program \n");

  /// 创建显示窗口
  namedWindow( window_name, CV_WINDOW_AUTOSIZE );

  /// 初始化输入参数
  top = (int) (0.05*src.rows); bottom = (int) (0.05*src.rows);
  left = (int) (0.05*src.cols); right = (int) (0.05*src.cols);
  dst = src;

  imshow( window_name, dst );

  while( true )
    {
      c = waitKey(500);

      if( (char)c == 27 )
        { break; }
      else if( (char)c == 'c' )
        { borderType = BORDER_CONSTANT; }
      else if( (char)c == 'r' )
        { borderType = BORDER_REPLICATE; }

      value = Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
      copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );

      imshow( window_name, dst );
    }

  return 0;
}

解释

  1. 首先申明程序中用到的变量:

    Mat src, dst;
    int top, bottom, left, right;
    int borderType;
    Scalar value;
    char* window_name = "copyMakeBorder Demo";
    RNG rng(12345);
    

    尤其要注意变量 rng ,这是一个随机数生成器, 用来产生随机边界色彩。

  2. 装载原图像 src:

    src = imread( argv[1] );
    
    if( !src.data )
    { return -1;
      printf(" No data entered, please enter the path to an image file \n");
    }
    
  3. 在简要说明了程序的使用方法后,创建一个显示窗口:

    namedWindow( window_name, CV_WINDOW_AUTOSIZE );
    
  4. 初始化边界宽度参数(topbottomleft 和 right)。我们将它们设定为图像 src 大小的5%。

    top = (int) (0.05*src.rows); bottom = (int) (0.05*src.rows);
    left = (int) (0.05*src.cols); right = (int) (0.05*src.cols);
    
  5. 程序进入 while 循环。 如果用户按’c’键或者 ‘r’键, 变量 borderType 分别取值 BORDER_CONSTANT 或 BORDER_REPLICATE :

    while( true )
     {
       c = waitKey(500);
    
       if( (char)c == 27 )
         { break; }
       else if( (char)c == 'c' )
         { borderType = BORDER_CONSTANT; }
       else if( (char)c == 'r' )
         { borderType = BORDER_REPLICATE; }
    
  6. 每个循环 (周期 0.5 秒), 变量 value 自动更新...

    value = Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
    

    为一个由 RNG 类型变量 rng 产生的随机数。 随机数的范围在 [0,255] 之间。

  7. 最后调用函数 copyMakeBorder 填充边界像素:

    copyMakeBorder( src, dst, top, bottom, left, right, borderType, value );
    

    接受参数:

    1. src: 原图像
    2. dst: 目标图像
    3. topbottomleftright: 各边界的宽度,此处定义为原图像尺寸的5%。
    4. borderType: 边界类型,此处可以选择常数边界或者复制边界。
    5. value: 如果 borderType 类型是 BORDER_CONSTANT, 该值用来填充边界像素。
  8. 显示输出图像

    imshow( window_name, dst );
    

结果

  1. 在编译上面的代码之后, 我们可以运行结果,将图片路径输入。 结果应该为:

    • 程序启动时边界类型为 BORDER_CONSTANT (0), 因此,一开始边界颜色任意变换。
    • 如果用户按 ‘r’ 键, 边界将会变成原图像边缘的拷贝。
    • 如果用户按 ‘c’ 键, 边界再次变为任意颜色。
    • 如果用户按 ‘ESC’ 键,程序退出。

    下面显示了几张截图演示了边界颜色如何改变,以及在边界类型为 BORDER_REPLICATE 时的情形:

    Final result after copyMakeBorder application

                                     

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值