2019-12-02 19:58:13 He_r_o 阅读数 146

数字图像处理:如何通过背景相减提取物体轮廓?

因为在数学建模比赛中需要用到数字图像处理,从0开始学习,自己把期间做的笔记整理下来。不积跬步无以至千里。

一、理论

1、图像的数字化采集

图像采样就是按照图像空间的坐标测量该位置上像素的灰度值,对连续图像f(x,y)进行等间隔采样:

公式1
f(x,y)=[f(0,0)f(0,1)f(0,N1)f(1,0)f(1,1)f(1,N1)f(M1,0)f(M1,1)f(M1,N1)] f(x, y)=\left[\begin{array}{lll}{f(0,0)} & {f(0,1)} & {\dots} & {f(0, N-1)} \\ {f(1,0)} & {f(1,1)} & {\dots} & {f(1, N-1)} \\ {\cdots} & {} & {} \\ {f(M-1,0)} & {f(M-1,1)} & {\dots} & {f(M-1, N-1)}\end{array}\right]

2、 高斯降噪

高斯降噪的原理是利用高斯函数作为模板,与原始图像进行卷积达到降噪的目的。

二维的高斯函数如下:

公式2
f(x,y)=(1/2πδ2)ex2+y22δ2 \mathrm{f}(\mathrm{x}, \quad \mathrm{y})=\left(1 / 2 \pi \delta^{2}\right) * \mathrm{e}^{-\frac{\mathrm{x}^{2}+\mathrm{y}^{2}}{2 \delta^{2}}}

3、图片灰度化

RGB颜色模式并不能反映图像的形态特征,只是从光学的原理上进行颜色的调配。图像灰度化处理可以作为图像处理的预处理步骤:

将RGB分量以不同的权值进行加权平均,得到较合理的灰度图像:

公式3
Gray(i,j)=0.299R(i,j)+0.578G(i,j)+0.114B(i,j) \operatorname{Gray}(i, j)=0.299 * R(i, j)+0.578 * G(i, j)+0.114 * B(i, j)

4、直方图均衡化

直方图均衡化是将原图像通过某种变换,得到一幅灰度直方图为均匀分布的新图像的方法,从而达到清晰图像的目的。

https://blog.csdn.net/spongebob1234/article/details/77778709
M为图像行数, 为图像列数, 为灰度值, 为灰度级的个数

5、伽马变换

在图像处理中,将漂白(相机过曝)的图片或者过暗(曝光不足)的图片,通过拉伸和压缩灰度级进行图片的灰度变换。
result(i,j)=c×img(i,j)γ result(i, j) = c{\times}img(i, j)^{\gamma}

6、背景相减

result(i,j)={0,dis{[(i,j),(x0,y0)]}>2701,img(i,j)img2(i,j)>100, others  \operatorname{result}(i, j)=\left\{\begin{array}{lr}{0,} & {\operatorname{dis}\left\{\left[(i, j),\left(x_{0}, y_{0}\right)\right]\right\}>270} \\ {1,} & {i m g(i, j)-i m g 2(i, j)>10} \\ {0,} & {\text { others }}\end{array}\right.

result表示结果矩阵, 为点 到点 的距离
img表示前景图像矩阵
img2表示背景图像矩阵

7、连通性

八连通或者四连通

二、代码实现

1、图片预处理

%imagePreprocessing.mlx 图片预处理文件
%路径变量
imgPath = "2019 APMCM Problem A Attachment";        % 图像库路径
imgPath2 = "colorGray"
imgOutPutPath = "gray"
histOutPutPath = "process_image"
%处理灰色图

imgDir  = dir(imgPath); % 遍历所有bmp格式文件
for i = 1 : length(imgDir) %遍历结构体
    if imgDir(i).name == "." || imgDir(i).name == ".."
        continue;
    end
%导入图片
    name = imgDir(i).name
    imgPathName = strcat(imgPath, "\", name)
    img = imread(imgPathName); %读取每张图片
    
%高斯降噪
    h_gaosi1=fspecial('gaussian',3,1);
    imageAfter=imfilter(img,h_gaosi1);
    imshow(imageAfter)
%灰度处理
grayImg = rgb2gray(img)  
grayImg = im2double(grayImg);
result = 1 * (grayImg .^ 2); 
saveImgName = strcat(imgOutPutPath, "\", "gray_", name)
imwrite(result, saveImgName);
imhist(result);
ylabel('出现频数');
end


%处理彩图
imgDir2  = dir(imgPath2); % 遍历所有bmp格式文件
for i = 1 : length(imgDir2) %遍历结构体
    if imgDir2(i).name == "." || imgDir2(i).name == ".."
        continue;
    end
%导入图片
    name = imgDir2(i).name
    imgPathName = strcat(imgPath2, "\", name)
    img = imread(imgPathName); %读取每张图片
    
%高斯降噪
    h_gaosi1=fspecial('gaussian',3,1);
    imageAfter=imfilter(img,h_gaosi1);
    imshow(imageAfter)
%灰度处理
grayImg = rgb2gray(img)
result = grayImg
saveImgName = strcat(imgOutPutPath, "\", "gray_", name)
imwrite(result, saveImgName);
imhist(result);
ylabel('出现频数');
end

2、背景相减

%ACPC.mxl 背景减除及二值化
clear;
clc;
% i1=imread('D:\MATLAB\ACPCM\IMG_BW\516_bw.bmp');
% i2=imread('D:\MATLAB\ACPCM\IMG_BW\610_bw.bmp');
% i1=rgb2gray(i1);
% i2=rgb2gray(i2);
i1=imread('D:\MATLAB\ACPCM\gray\gray_0502.bmp');
i2=imread('D:\MATLAB\ACPCM\gray\gray_0610.bmp');
[m,n]=size(i1);
im1=double(i1);
im2=double(i2);
i3=zeros(size(i1));
for i=1:m
    for j=1:n
        if (((i-561)*(i-561))+((j-1047)*(j-1047))) > 270*270%研究区域半径占270个像素点
              i3(i,j)=0;
        elseif abs((im1(i,j))-(im2(i,j))) > 8%最佳阈值在8~30之间
              i3(i,j)=1;
        else 
              i3(i,j)=0;
        end
    end
end
for i=10:m-10
    for j=10:n-10
        if i3(i-1,j-1)==1 && i3(i,j-1)==1 && i3(i-1,j)==1 && i3(i+1,j+1)==1  && i3(i-1,j+1)==1 && i3(i+1,j)==1 && i3(i,j+1)==1 && i3(i+1,j-1)==1 
            i3(i,j)=1;
        elseif i3(i-1,j-1)==0 && i3(i,j-1)==0 && i3(i-1,j)==0 && i3(i+1,j+1)==0  && i3(i-1,j+1)==0 && i3(i+1,j)==0 && i3(i,j+1)==0 && i3(i+1,j-1)==0 
            i3(i,j)=0; 
        end
    end
end
figure,imshow(i3);title('the result of 0502')
imfill(i3, 'hole');
contour = bwperim(i3);
figure,imshow(contour);title('the contour of 0502')

2017-08-16 03:54:51 pianzang5201 阅读数 341

一、什么是轮廓

轮廓可以简单地解释为连接所有连续点(沿着边界),具有相同颜色或强度的曲线。轮廓是形状分析和物体检测和识别的有用工具。

• 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或者 Canny 边界检测。 

• 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图像的话,应该将原始图像存储到其他变量中。 

• 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。你应该记住, 要找的物体应该是白色而背景应该是黑色。


二、如何在一个二值图像中查找轮廓: 

import numpy as np  
import cv2  
  
img = cv2.imread('F:/rectangle.jpg')  
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  
ret, thresh = cv2.threshold(imgray,127,255,0)  
contours, hierachy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

函数 cv2.findContours() 有三个参数,第一个是输入图像第二个是轮廓检索模式第三个是轮廓近似方法。这个参数如果被设置为 cv2.CHAIN_APPROX_NONE,所有的边界点 都会被存储。但是我们真的需要这么多点吗?例如,当我们找的边界是一条直 线时。你用需要直线上所有的点来表示直线吗?不是的,我们只需要这条直线 的两个端点而已。这就是 cv2.CHAIN_APPROX_SIMPLE 要做的。它会将轮廓上的冗余点都去掉,压缩轮廓,从而节省内存开支。 

返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构,官方给的也是三个但是自己实践的情况是两个返回值没有第一个,也可能是编辑器的问题,目前还没有在别的平台试过,如果试过可能会自己删除这一句话。轮廓(第二个返回值)是一个 Python 列表,其中存储这图像中的所有轮廓。每一个轮廓都是一个 Numpy 数组,包含对象边界点(x,y)的坐标。

三、绘制轮廓 

函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供 的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一 个 Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设 置为 -1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。 

cv2.drawContours(img, contours, -1, (0,255,0), 3) #绘制边缘  
  
cv2.imshow('img',img)  
cv2.waitKey()  
cv2.destroyAllWindows()

官方给的结果是:第一个图显示使用 cv2.CHAIN_APPROX_NONE 的效果, 一共 734 个点。第二个图是使用 cv2.CHAIN_APPROX_SIMPLE 的结 果,只有 4 个点。

实践结果确实只有一种情况: cv2.CHAIN_APPROX_NONE()和cv2.CHAIN_APPROX_SIMPLE()效果一样。

拓展:

自己写的程序:

#coding:utf-8
import numpy as np
import cv2

img = cv2.imread('F:/manyRec.jpg')
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray,127,255,0)
contours, hierachy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)


for i in range(int(len(contours))): #绘制几个形状的边缘
    cnt = contours[i]
    cv2.drawContours(img, [cnt], -1, (i*40, 255-i*40, 255-i*40), 3) #用不同的颜色


cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()

效果图:



2019-04-11 13:43:00 qq_35789421 阅读数 119

图像中目标物体的形状检测是图像识别中重要的技术之一,对物体进行检测并提取首先需要做的就是提取物体的轮廓信息,然后再通过轮廓点集特征选择相应的算法进行处理,最后可得到物体的形状信息。轮廓形状是我们看到物体最开始的印象,轮廓提取的原理是通过对原图像进行二值化,利用边缘点连接的层次差别,提取位于数结构特征高的区域点集构成的集合,这部分最可能是物体的轮廓。

OpenCV中提供了函数findContours()用于对物体轮廓进行检测,findContours相关参数的含义:

void findContours(InputArray image,OutputArrayOfArrays contours,OutputArray hierarchy,int mode,int method,Point offsetPoint())

参数image为输入图像,8位单通道二值图像;contours为检测到的轮廓,每个轮廓都在向量中;参数hierarchy为包含图像拓扑结构的信息;mode为可选获取轮廓的方法,常用参数为外轮廓CV_PETR_EXTERNAL,CV_PETR_LIST为检测所有轮廓的不包含继承关系,CV_RETR_TREE为检测所有轮廓的包含继承关系,CV_PETR_CCOMP为检测所有轮廓,但是仅仅建立两层包含关系;method为轮廓近似的方法,参数设置为CV_CHAIN_APPROX_NONE表示把轮廓上所有的点存储,CV_CHAIN_APPROX_SIMPLE表示只存储水平、垂直即对角直线的起始点;offest为可选的偏移量。

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

using namespace cv;
using namespace std;

Mat srcImage;
Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

void thresh_callback(int,void*)
{
    Mat caany_output;
    vector<vector<point>> contours;
    vector<Vec4i> hierarchy;
    Canny(src_gray,caany_output,thresh,thresh*2,3);          
 findContours(caany_output,contours,hierarchy,CV_PETR_TREE,CV_CHAIN_APPROX_SIMPLE,Point(0,0));
    Mat drawing = Mat::zeros(caany_output.size(),CV_8UC3);
    for(int i = 0;i<contours.size();i++)
    {
        Scalar color = Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255));
        drawContours(drwing,contours,i,color,2,8,hierarchy,0,Point());
    }
    namedWindow("contours",CV_WINDOW_AUTOSIZE);
    imshow("contours",drawing);
}

int main()
{
    Mat srcImage = imread();
    cvtcolor(srcImage,src_gray,CV_BGR2GRAY);
    blur(src_gray,src_gray,Size(3,3));
    char* source_window = "srcImage";
    namedWindow(source_window,CV_WINDOW_AUTOSIZE);
    imshow(source_window,srcImage);
    createTrackbar("threth:","srcImage",&thresh,max_thresh,thresh_callback);
    thresh_callback(0,0);
    waitkey(0);
    return 0 ;
}

 

2019-02-10 22:05:13 qq_36771850 阅读数 354

了解图像处理的基本概念和基本的函数调用
stretchlim

图像处理的级别划分

低级:包括原始操作,如降低噪声的图像预处理,对比度增强和图像锐化。特点是输入和输出都是图像。
中级:包括诸如分割这样的任务,即把图像分为区域或目标,然后对这些目标进行描述,以便把它们简化为适合计算机处理的形式,并对单个目标进行分类(识别)。特点是输入通常为图像,但输出是从这些图像中提取的属性(如边缘、轮廓和单个物体的特征)。
高级:高级处理通过执行通常与人类视觉相关的感知函数,来对识别的对象进行总体确认。

灰度级和通道

一般的RGB图像,也就是彩色图像都是一个三维的矩阵,每一个矩阵代表了一个颜色通道,而灰度级则是一个矩阵中不同数字的大小,例入一个8bit的图像中,每一个矩阵的数值范围是【0 255】(2*8-1),数字越大,则像素越亮,映射到通道上颜色越鲜艳。最后我们得到的彩色图像就是三个矩阵(RGB)对应到不同颜色通道的叠加。

>> f = imread('0.jpg');
>> R = f(:,:,1);
>> G = f(:,:,2);
>> B = f(:,:,3);
>> subplot(221),imshow(f);title('原图');
>> subplot(222),imshow(R);title('R通道');
>> subplot(223),imshow(G);title('G通道');
>> subplot(224),imshow(B);title('B通道');
>> g = imadjust(f,[100 200] [0 255]);

RGB三通道

函数的调用

  • mat2gray:将图像转化为double类型,并将其灰度标定到范围【0,1】。
  • imadjust g = imadjust(f,[low_in high_in],[low_out high_out],gamma)
    将函数图像f中的灰度值映射为g中的新值,即将low_in至high_in之间的值映射到low_out至high_out之间的值。low_in以下与high_in以上的值则被截去,即low_in以下的值映射为low_out,high_in以上的值映射为high_out。中间的两个参数所有输入值必须限定在【0 1】之间。gamma值小于1,图像映射被加权至较高(较亮)的输出值,即图像较亮,gamma大于1,则映射加权至较低(较暗)的输出值,即图像较暗,默认为1,不加权。
>> subplot(221),imshow(f);title('原图');
>> g = imadjust(f,[0 1],[1 0],1);
>> subplot(222),imshow(g);title('[0 1] [1 0]');
>> g = imadjust(f,[0.5 0.75],[0 1],1);
>> subplot(223),imshow(g);title('[0.5 0.75] [0 1]');
>> g = imadjust(f,[],[],2);
>> subplot(224),imshow(g);title('[] [] 2');

imadjust

  • stretchlim
    stretchlim主要用于自适应找到一个分割阈值向量来改变一幅图像的对比度,其通用调用格式如下:
    low_high = stretchlim(f,tol)
    可得到一个灰度变换自适应最佳阈值,不用人为规定,再将其带入imadjust函数中便可实现灰度图像对比度的增加或减弱。
>> g = imadjust(f,stretchlim(f),[]);
>> imshow(g);

stretchlim

2018-05-09 19:44:31 qq_40909394 阅读数 106

在图像中寻找轮廓

目标

在这个教程中你将学到如何:

原理

例程

教程的代码在下面给出. 你也可以从 这里 下载

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

using namespace cv;
using namespace std;

Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

/// Function header
void thresh_callback(int, void* );

/** @function main */
int main( int argc, char** argv )
{
  /// 加载源图像
  src = imread( argv[1], 1 );

  /// 转成灰度并模糊化降噪
  cvtColor( src, src_gray, CV_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );

  /// 创建窗体
  char* source_window = "Source";
  namedWindow( source_window, CV_WINDOW_AUTOSIZE );
  imshow( source_window, src );

  createTrackbar( " Canny thresh:", "Source", &thresh, max_thresh, thresh_callback );
  thresh_callback( 0, 0 );

  waitKey(0);
  return(0);
}

/** @function thresh_callback */
void thresh_callback(int, void* )
{
  Mat canny_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;

  /// 用Canny算子检测边缘
  Canny( src_gray, canny_output, thresh, thresh*2, 3 );
  /// 寻找轮廓
  findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// 绘出轮廓
  Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
     }

  /// 在窗体中显示结果
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
}

例程说明

结果

  1. 原图和检测到的轮廓如下:

    contour_0

    contour_1



计算物体的凸包

目标

在这个教程中你将学习到如何:

原理

例程

教程的代码在下面给出. 你也可以从 这里 下载

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

 using namespace cv;
 using namespace std;

 Mat src; Mat src_gray;
 int thresh = 100;
 int max_thresh = 255;
 RNG rng(12345);

 /// Function header
 void thresh_callback(int, void* );

/** @function main */
int main( int argc, char** argv )
 {
   /// 加载源图像
   src = imread( argv[1], 1 );

   /// 转成灰度图并进行模糊降噪
   cvtColor( src, src_gray, CV_BGR2GRAY );
   blur( src_gray, src_gray, Size(3,3) );

   /// 创建窗体
   char* source_window = "Source";
   namedWindow( source_window, CV_WINDOW_AUTOSIZE );
   imshow( source_window, src );

   createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
   thresh_callback( 0, 0 );

   waitKey(0);
   return(0);
 }

 /** @function thresh_callback */
 void thresh_callback(int, void* )
 {
   Mat src_copy = src.clone();
   Mat threshold_output;
   vector<vector<Point> > contours;
   vector<Vec4i> hierarchy;

   /// 对图像进行二值化
   threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );

   /// 寻找轮廓
   findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

   /// 对每个轮廓计算其凸包
   vector<vector<Point> >hull( contours.size() );
   for( int i = 0; i < contours.size(); i++ )
      {  convexHull( Mat(contours[i]), hull[i], false ); }

   /// 绘出轮廓及其凸包
   Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
   for( int i = 0; i< contours.size(); i++ )
      {
        Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
        drawContours( drawing, hull, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
      }

   /// 把结果显示在窗体
   namedWindow( "Hull demo", CV_WINDOW_AUTOSIZE );
   imshow( "Hull demo", drawing );
 }

例程说明

结果

  1. 原图和结果图如下:

    Hull_0

    Hull_1







创建包围轮廓的矩形和圆形边界框

目标

在这节教程中您将学到:

  • 使用OpenCV函数 boundingRect 来计算包围轮廓的矩形框.
  • 使用OpenCV函数 minEnclosingCircle 来计算完全包围已有轮廓最小圆.

原理

代码

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

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

using namespace cv;
using namespace std;

Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

/// 函数声明
void thresh_callback(int, void* );

/** @主函数 */
int main( int argc, char** argv )
{
  /// 载入原图像, 返回3通道图像
  src = imread( argv[1], 1 );

  /// 转化成灰度图像并进行平滑
  cvtColor( src, src_gray, CV_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );

  /// 创建窗口
  char* source_window = "Source";
  namedWindow( source_window, CV_WINDOW_AUTOSIZE );
  imshow( source_window, src );

  createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
  thresh_callback( 0, 0 );

  waitKey(0);
  return(0);
}

/** @thresh_callback 函数 */
void thresh_callback(int, void* )
{
  Mat threshold_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;

  /// 使用Threshold检测边缘
  threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
  /// 找到轮廓
  findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// 多边形逼近轮廓 + 获取矩形和圆形边界框
  vector<vector<Point> > contours_poly( contours.size() );
  vector<Rect> boundRect( contours.size() );
  vector<Point2f>center( contours.size() );
  vector<float>radius( contours.size() );

  for( int i = 0; i < contours.size(); i++ )
     { approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
       boundRect[i] = boundingRect( Mat(contours_poly[i]) );
       minEnclosingCircle( contours_poly[i], center[i], radius[i] );
     }


  /// 画多边形轮廓 + 包围的矩形框 + 圆形框
  Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
       rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
       circle( drawing, center[i], (int)radius[i], color, 2, 8, 0 );
     }

  /// 显示在一个窗口
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
}

代码说明

结果

  1. 如下所示:

    BRC_0

    BRC_1






为轮廓创建可倾斜的边界框和椭圆

目标

在这个教程中你将学习到如何:

原理

例程

例程的代码在下面显示. 你也可以从 这里 下载

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

using namespace cv;
using namespace std;

Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

/// Function header
void thresh_callback(int, void* );

/** @function main */
int main( int argc, char** argv )
{
  /// 加载源图像
  src = imread( argv[1], 1 );

  /// 转为灰度图并模糊化
  cvtColor( src, src_gray, CV_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );

  /// 创建窗体
  char* source_window = "Source";
  namedWindow( source_window, CV_WINDOW_AUTOSIZE );
  imshow( source_window, src );

  createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
  thresh_callback( 0, 0 );

  waitKey(0);
  return(0);
}

/** @function thresh_callback */
void thresh_callback(int, void* )
{
  Mat threshold_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;

  /// 阈值化检测边界
  threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
  /// 寻找轮廓
  findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// 对每个找到的轮廓创建可倾斜的边界框和椭圆
  vector<RotatedRect> minRect( contours.size() );
  vector<RotatedRect> minEllipse( contours.size() );

  for( int i = 0; i < contours.size(); i++ )
     { minRect[i] = minAreaRect( Mat(contours[i]) );
       if( contours[i].size() > 5 )
         { minEllipse[i] = fitEllipse( Mat(contours[i]) ); }
     }

  /// 绘出轮廓及其可倾斜的边界框和边界椭圆
  Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       // contour
       drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
       // ellipse
       ellipse( drawing, minEllipse[i], color, 2, 8 );
       // rotated rectangle
       Point2f rect_points[4]; minRect[i].points( rect_points );
       for( int j = 0; j < 4; j++ )
          line( drawing, rect_points[j], rect_points[(j+1)%4], color, 1, 8 );
     }

  /// 结果在窗体中显示
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
}

说明

结果

  1. 结果如下图:

    BRE_0

    BRE_1






轮廓矩

目标

在这节教程中您将学到:

  • 使用OpenCV函数 moments 计算图像所有的矩(最高到3阶)
  • 使用OpenCV函数 contourArea 来计算轮廓面积
  • 使用OpenCV函数 arcLength 来计算轮廓或曲线长度

原理

代码

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

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

using namespace cv;
using namespace std;

Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

/// 函数声明
void thresh_callback(int, void* );

/** @主函数 */
int main( int argc, char** argv )
{
  /// 读入原图像, 返回3通道图像数据
  src = imread( argv[1], 1 );

  /// 把原图像转化成灰度图像并进行平滑
  cvtColor( src, src_gray, CV_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );

  /// 创建新窗口
  char* source_window = "Source";
  namedWindow( source_window, CV_WINDOW_AUTOSIZE );
  imshow( source_window, src );

  createTrackbar( " Canny thresh:", "Source", &thresh, max_thresh, thresh_callback );
  thresh_callback( 0, 0 );

  waitKey(0);
  return(0);
}

/** @thresh_callback 函数 */
void thresh_callback(int, void* )
{
  Mat canny_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;

  /// 使用Canndy检测边缘
  Canny( src_gray, canny_output, thresh, thresh*2, 3 );
  /// 找到轮廓
  findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// 计算矩
  vector<Moments> mu(contours.size() );
  for( int i = 0; i < contours.size(); i++ )
     { mu[i] = moments( contours[i], false ); }

  ///  计算中心矩:
  vector<Point2f> mc( contours.size() );
  for( int i = 0; i < contours.size(); i++ )
     { mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }

  /// 绘制轮廓
  Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
       circle( drawing, mc[i], 4, color, -1, 8, 0 );
     }

  /// 显示到窗口中
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );

  /// 通过m00计算轮廓面积并且和OpenCV函数比较
  printf("\t Info: Area and Contour Length \n");
  for( int i = 0; i< contours.size(); i++ )
     {
       printf(" * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f \n", i, mu[i].m00, contourArea(contours[i]), arcLength( contours[i], true ) );
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
       circle( drawing, mc[i], 4, color, -1, 8, 0 );
     }
}

代码说明

结果

  1. 如下所示:

    MU_0

    MU_1

    MU_2





多边形测试

目的

本教程指导用户:

理论

代码

本教程代码如下所示. 用户也可以点击 这里下载

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

using namespace cv;
using namespace std;

/** @function main */
int main( int argc, char** argv )
{
  /// 创建一个图形     const int r = 100;
  Mat src = Mat::zeros( Size( 4*r, 4*r ), CV_8UC1 );

  /// 绘制一系列点创建一个轮廓:
  vector<Point2f> vert(6);

  vert[0] = Point( 1.5*r, 1.34*r );
  vert[1] = Point( 1*r, 2*r );
  vert[2] = Point( 1.5*r, 2.866*r );
  vert[3] = Point( 2.5*r, 2.866*r );
  vert[4] = Point( 3*r, 2*r );
  vert[5] = Point( 2.5*r, 1.34*r );

  /// 在src内部绘制
  for( int j = 0; j < 6; j++ )
     { line( src, vert[j],  vert[(j+1)%6], Scalar( 255 ), 3, 8 ); }

  /// 得到轮廓
  vector<vector<Point> > contours; vector<Vec4i> hierarchy;
  Mat src_copy = src.clone();

  findContours( src_copy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);

  /// 计算到轮廓的距离
  Mat raw_dist( src.size(), CV_32FC1 );

  for( int j = 0; j < src.rows; j++ )
     { for( int i = 0; i < src.cols; i++ )
          { raw_dist.at<float>(j,i) = pointPolygonTest( contours[0], Point2f(i,j), true ); }
     }

  double minVal; double maxVal;
  minMaxLoc( raw_dist, &minVal, &maxVal, 0, 0, Mat() );
  minVal = abs(minVal); maxVal = abs(maxVal);

  /// 图形化的显示距离
  Mat drawing = Mat::zeros( src.size(), CV_8UC3 );

  for( int j = 0; j < src.rows; j++ )
     { for( int i = 0; i < src.cols; i++ )
          {
            if( raw_dist.at<float>(j,i) < 0 )
              { drawing.at<Vec3b>(j,i)[0] = 255 - (int) abs(raw_dist.at<float>(j,i))*255/minVal; }
            else if( raw_dist.at<float>(j,i) > 0 )
              { drawing.at<Vec3b>(j,i)[2] = 255 - (int) raw_dist.at<float>(j,i)*255/maxVal; }
            else
              { drawing.at<Vec3b>(j,i)[0] = 255; drawing.at<Vec3b>(j,i)[1] = 255; drawing.at<Vec3b>(j,i)[2] = 255; }
          }
     }

  /// 创建窗口显示结果
  char* source_window = "Source";
  namedWindow( source_window, CV_WINDOW_AUTOSIZE );
  imshow( source_window, src );
  namedWindow( "Distance", CV_WINDOW_AUTOSIZE );
  imshow( "Distance", drawing );

  waitKey(0);
  return(0);
}

解释

结果

  1. 输出如下:

    PPT_0

    PPT_1




from: http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/table_of_content_imgproc/table_of_content_imgproc.html#table-of-content-imgproc

图像轮廓

阅读数 135

没有更多推荐了,返回首页