OpenCV 之 图像分割 (一)

1  基于阈值

1.1  基本原理

灰度阈值化,是最简单也是速度最快的一种图像分割方法,广泛应用在硬件图像处理领域 (例如,基于 FPGA 的实时图像处理)。

假设输入图像为 f,输出图像为 g,则经过阈值化处理的公式如下:

$\quad g(i, j) = \begin{cases} 1 & \text{当 f(i, j) ≥ T 时} \\0 & \text{当 f(i, j) < T 时} \\ \end{cases} $

也即,遍历图像中的所有像素,当像素值 f (i, j) ≥ T 时,分割后的图像元素 g (i, j) 是物体像素,否则为背景像素。

如果各个物体之间彼此不接触,并且物体灰度和背景灰度之间差别比较明显时,灰度阈值化便是非常合适的分割方法。

1.2  cv::threshold 函数 

OpenCV 中的阈值化函数为 threshold,其使用如下所示:

double cv::threshold (
    InputArray  src,   // 输入图像 (单通道,8位或32位浮点型)
    OutputArray  dst,  // 输出图像 (大小和类型,都同输入)
    double    thresh, // 阈值
    double    maxval, // 最大灰度值(使用 THRESH_BINARY 和 THRESH_BINARY_INV类型时)
    int      type   // 阈值化类型(THRESH_BINARY, THRESH_BINARY_INV; THRESH_TRUNC; THRESH_TOZERO, THRESH_TOZERO_INV)
)

1) THRESH_BINARY

$\qquad dst(x, y) = \begin{cases} maxval & \text{if src(x, y) > thresh} \\0 & \text{otherwise} \\ \end{cases} $

2) THRESH_TRUNC

$\qquad dst(x, y) = \begin{cases} threshold & \text{if src(x, y) > thresh} \\src(x, y) & \text{otherwise} \\ \end{cases} $

3) THRESH_TOZERO

$\qquad dst(x, y) = \begin{cases} src(x, y) & \text{if src(x, y) > thresh} \\0 & \text{otherwise} \\ \end{cases} $

1.3  示例

下面是阈值化类型和阈值可选的代码示例,摘自 OpenCV 例程,略作修改

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"

using namespace cv;

int threshold_value = 0;
int threshold_type = 3;
int const max_value = 255;
int const max_type = 4;
int const max_BINARY_value = 255;

Mat src, src_gray, dst;
const char* window_name = "Threshold Demo";

const char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";
const char* trackbar_value = "Value";

void Threshold_Demo(int, void*);

int main( int, char** argv )
{
  // 读图
  src = imread("Musikhaus.jpg",IMREAD_COLOR);
  if( src.empty() )
      return -1;

  // 转化为灰度图
  cvtColor( src, src_gray, COLOR_BGR2GRAY );

  // 显示窗口
  namedWindow( window_name, WINDOW_AUTOSIZE );

  // 滑动条 - 阈值化类型
  createTrackbar( trackbar_type, window_name, &threshold_type,max_type,Threshold_Demo);
  // 滑动条 - 阈值
  createTrackbar( trackbar_value,window_name, &threshold_value,max_value,Threshold_Demo);

  Threshold_Demo(0, 0);

  waitKey(0);
}

void Threshold_Demo(int, void*)
{
    /* 0: Binary
    1: Binary Inverted
    2: Threshold Truncated
    3: Threshold to Zero
    4: Threshold to Zero Inverted
    */
    threshold(src_gray, dst, threshold_value, max_BINARY_value, threshold_type);
    imshow(window_name, dst);
}

2  基于边缘

前一篇 <OpenCV 之 边缘检测> 中,介绍了三种常用的边缘检测算子: Sobel, Laplace 和 Canny 算子。

实际上,边缘检测的结果是一个个的点,并不能作为图像分割的结果,必须采用进一步的处理,将边缘点沿着图像的边界连接起来,形成边缘链。

2.1  轮廓函数

OpenCV 中,可在图像的边缘检测之后,依次使用 cv::findContours 和 cv::drawContours 函数,寻找到轮廓并将其画出

void cv::findContours (
    InputOutputArray      image,       // 源图像
    OutputArrayOfArrays   contours,    // 检测到的轮廓
    OutputArray           hierarchy,   //
    int       mode,            // 轮廓获取模式 (RETR_EXTERNAL, RETR_LIST, RETR_CCOMP,RETR_TREE, RETR_FLOODFILL)
    int       method,          // 轮廓近似算法 (CHAIN_APPROX_NONE, CHAIN_APPROX_SIMPLE, CHAIN_APPROX_TC89_L1, CHAIN_APPROX_TC89_KCOS)
    Point     offset = Point() // 轮廓偏移量
)

cv::drawContours 函数各参数如下:

void cv::drawContours (
    InputOutputArray     image,         // 目标图像
    InputArrayOfArrays   contours,      // 所有的输入轮廓
    int               contourIdx,      //
    const Scalar &     color,           //  轮廓颜色
    int          thickness = 1,         //  轮廓线厚度
    int          lineType = LINE_8,     //
    InputArray   hierarchy = noArray(), //
    int          maxLevel = INT_MAX,    //
    Point        offset = Point()       //
)     

2.2  例程

  代码摘自 OpenCV 例程,略有修改

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"

using namespace cv;
using namespace std;

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

void thresh_callback(int, void* );

int main( int, char** argv )
{
  // 读图
  src = imread("Pillnitz.jpg", IMREAD_COLOR);
  if (src.empty())
      return -1;

  // 转化为灰度图
  cvtColor(src, src_gray, COLOR_BGR2GRAY );
  blur(src_gray, src_gray, Size(3,3) );

  // 显示
  namedWindow("Source", WINDOW_AUTOSIZE );
  imshow( "Source", src );

  // 滑动条
  createTrackbar("Canny thresh:", "Source", &thresh, max_thresh, thresh_callback );

  // 回调函数
  thresh_callback( 0, 0 );

  waitKey(0);
}

// 回调函数
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, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );

  Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3);

  // 画出轮廓
  for( size_t i = 0; i< contours.size(); i++ ) {
      Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
      drawContours( drawing, contours, (int)i, color, 2, 8, hierarchy, 0, Point() );
  }

  namedWindow( "Contours", WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
}

以 Dresden 的 Schloss Pillnitz 为源图,输出如下:

参考资料:

OpenCV Tutorials, imgproc module, Basic Thresholding Operations

OpenCV Tutorials, imgproc module, Finding contours in your image

<图像处理、分析与机器视觉_第3版>  第 6 章

Topological structural analysis of digitized binary images by border following [J], Satoshi Suzuki, 1985

时间: 2024-12-07 17:17:48

OpenCV 之 图像分割 (一)的相关文章

opencv 金字塔图像分割

我所知的opencv中分割函数:watershed(只是看看效果,不能返回每类pixel类属),cvsegmentImage,cvPyrSegmentation(返回pixel类属) 金字塔分割原理篇在这里,本文只提供代码. Segment函数: [cpp] view plain copy #include<cv.h> #include <cvaux.h> #include <opencv\cxcore.hpp> #include <opencv.hpp>

opencv实现图像分割,分离前景和背景

简介 如题,本篇就是讲解和使用opencv函数grabcut,来实现图像前景与背景的分离. 函数原型 1.opencv官方介绍:opencv官方grabcut介绍 2.网上童鞋翻译解释:学习OpenCV--学习grabcut算法 3.大致内容如下: 函数原型: void grabCut(InputArray img, InputOutputArray mask, Rect rect, InputOutputArray bgdModel, InputOutputArray fgdModel, in

opencv实现图像分割,分离前景和背景(2)

简介 如题,本篇是在前一篇的基础上进一步讲解的第三个图像背景分离例子. 实例介绍 这个例子是在上一个加入鼠标操作实例的进一步操作. 本例:可以在鼠标选框完成之后,1.通过shift+鼠标右键来选择设置图像对应位置为前景. 2.通过ctrl +鼠标右键来选择设置图像对应位置为背景景. 3.按下键值'n',进行图像背景分离计算,并显示结果. 4.按下键值'esc',退出程序. 实例讲解 具体代码 #include "opencv2/highgui/highgui.hpp" #include

图像金字塔及其在 OpenCV 中的应用范例(下)

前言 本文将主要讲解如何使用 OpenCV 实现图像分割,这也是图像金字塔在 OpenCV 中的一个重要应用. 关于图像分割 在计算机视觉领域,图像分割(Segmentation)指的是将数字图像细分为多个图像子区域(像素的集合)(也被称作超像素)的过程.图像分割的目的是简化或改变图像的表示形式,使得图像更容易理解和分析.[1]图像分割通常用于定位图像中的物体和边界(线,曲线等).更精确的,图像分割是对图像中的每个像素加标签的一个过程,这一过程使得具有相同标签的像素具有某种共同视觉特性. 图像分

金字塔图像分割原理解析与示例[opencv]

图像分割指的是将数字图像细分为多个图像子区域的过程,在OpenCv中实现了三种跟图像分割相关的算法,它们分别是:金字塔分割算法,分水岭分割算法以及均值漂移分割算法.它们的使用过程都很简单,刚开始学习opencv,先记录一下我对金字塔分割原理的理解吧. 金字塔分割算法 金字塔分割算法由cvPrySegmentation所实现,该函数的使用还是比较简单:需要注意的是图像的尺寸以及金字塔的层数,图像的宽度和高度必须能被2整除,能够被2整除的次数决定了金字塔的最大层数.下面的代码演示了如何校验金字塔层数

基于GraphCuts图割算法的图像分割----OpenCV代码与实现

部分代码与文档是早些时候收集的,出处找不到了,还请原作者看到后联系注明. 图切算法是组合图论的经典算法之一.近年来,许多学者将其应用到图像和视频分割中,取得了很好的效果.本文简单介绍了图切算法和交互式图像分割技术,以及图切算法在交互式图像分割中的应用. 图像分割指图像分成各具特性的区域并提取出感兴趣目标的技术和过程,它是由图像处理到图像分析的关键步骤,是一种基本的计算机视觉技术.只有在图像分割的基础上才能对目标进行特征提取和参数测量,使得更高层的图像分析和理解成为可能.因此对图像分割方法的研究具

Python下opencv使用笔记(十二)(k均值算法之图像分割)

k均值(kmeans)聚类是一种最为简单的聚类方法,直接根据数据点之间的距离(欧氏距离,几何距离等等)来划分数据是属于哪一类的,当所有数据点所属的类别不在变化的时候,聚类也就完成了.详细原理可索引下面一个博客: 聚类分析笔记-K均值matlab算法(一) 关于kmeans再谈几点认识: 重要的一点:聚类数目的问题.有的聚类.分类问题已经限制好了要聚类成几类,也就是聚类数目一定,那么这种聚类通常简单些,直接规定聚类数就好了.而有的聚类问题不知道分成几类才好,这个时候怎么办?那么就需要找到一种评价指

opencv 5 图像轮廓与图像分割修复 2 使用多边形将轮廓包围

使用多边形将轮廓包围 返回外部矩阵边界(boundingRect()函数) 寻找最小包围矩形(minAreaRect()函数) 寻找最小包围圆形(minEnclosingCircle函数) 用椭圆拟合二维点集(fitEllipse()函数) 逼近多边形曲线(approxPolyDP()函数) 基础示例:创建包围轮廓的矩形边界 #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp

opencv::基于距离变换与分水岭的图像分割

什么是图像分割 图像分割(Image Segmentation)是图像处理最重要的处理手段之一 图像分割的目标是将图像中像素根据一定的规则分为若干(N)个cluster集合,每个集合包含一类像素. 根据算法分为监督学习方法和无监督学习方法,图像分割的算法多数都是无监督学习方法 - KMeans 距离变换常见算法有两种 - 不断膨胀/腐蚀得到 - 基于倒角距离 分水岭变换常见的算法 - 基于浸泡理论实现 cv::distanceTransform( InputArray src, OutputAr