openCV实现图像分割

本次实验为大家分享了openCV实现图像分割的具体实现代码,供大家参考,具体内容如下

一.实验目的

进一步理解图像的阈值分割方法和边缘检测方法的原理。
掌握图像基本全局阈值方法和最大类间方差法(otsu法)的原理并编程实现。
编程实现图像的边缘检测。

二.实验内容和要求

编程实现图像阈值分割(基本全局阈值方法和otsu法)和边缘检测。

三.实验主要仪器设备和材料

计算机,VS2017+OpenCV

四.实验原理与方法

图像的阈值分割的基本原理

图像的二值化处理图像分割中的一个主要内容,就是将图像上的点的灰度置为0或255,也就是讲整个图像呈现出明显的黑白效果。用I表示原图,R表示二值化后的图,则二值化的过程可以用以下公式表示:

thr表示选取的阈值。二值化的过程就是当原图的像素灰度值大于阈值就将其变白,否则就将其变黑。即将256个亮度等级的灰度图像通过适当的阀值选取而将图像变为二个级别灰度级,这样只有二个灰度级的图像在图像处理分析过程中占有非常重要的地位,特别是在实用的图像处理中。
根据对全图使用统一阈值还是对不同区域使用不同阈值,可以分为全局阈值方法(global thresholding)和局部阈值方法(local thresholding,也叫做自适应阈值方法adaptive thresholding);这种与坐标相关的阈值也叫动态阈值,具体的方法,可以参考相关的图像处理书籍。

1、基本全局阈值方法,即在整个图像中所有的象素点,其阈值thr相同,具体步骤为:

(1)选取一个初始估计值T;
(2)用T分割图像。这样便会生成两组像素集合:G1由所有灰度值大于T的像素组成,而G2由所有灰度值小于或等于T的像素组成。
(3)对G1和G2中所有像素计算平均灰度值u1和u2。
(4)计算新的阈值:T=(u1 + u2)/2。
(5)重复步骤(2)到(4),直到得到的T值之差小于一个事先定义的参数T0。

2、Otsu方法的算法步骤为:

(1)先计算图像的归一化直方图;
(2)i表示分类的阈值,也即一个灰度级,从0开始迭代;
(3)通过归一化的直方图,统计0~i 灰度级的像素(背景像素) 所占整幅图像的比例w0,并统计背景像素的平均灰度u0;统计i~255灰度级的像素(前景像素) 所占整幅图像的比例w1,并统计前景像素的平均灰度u1;
(4)计算前景像素和背景像素的方差 g = w0w1(u0-u1) (u0-u1)
(5)i++,直到i为256时结束迭代;
(6)将最大g相应的i值作为图像的全局阈值。

边缘检测

图像中边缘的检测可以借助一阶和二阶微分实现,常见的一阶边缘检测算子包括Roberts算子、Prewitt算子和Sobel算子,二阶算子主要是Laplacian算子,由于受噪声影响比较大,往往在使用之前先对图像进行平滑处理,LOG算子就是先对图像进行高斯平滑,然后进行拉普拉斯变换并求零交叉点。Canny算子是最优的边缘检测算子。

五.实验内容

1、图像的阈值分割:

图像为车牌图像,编写代码实现基本全局阈值法和Otsu法,比较分割结果。

2、边缘检测

用边缘检测算子对车牌图像进行处理,可以用梯度算子、Laplacian算子或Canny算子(Canny算子可以直接用OpenCV函数)。比较先阈值分割后边缘检测和直接对图像进行边缘检测这两种情况的结果是否有差别。
注意:这里提取灰度边缘即可。

代码:

#include "pch.h"
#include <iostream> 

#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

// 拉普拉斯锐化函数
void LaplacianSharpDeal(const Mat &src, Mat &dst) {
 if (!src.data)return;
 for (int i = 0; i < src.rows; ++i)
  for (int j = 0; j < src.cols; ++j) {
   float a;
   if (i > 1 && i < src.rows - 1 && j > 1 && j < src.cols - 1) {
    a = 5 * (float)src.at<uchar>(i, j) - (float)src.at<uchar>(i - 1, j) - (float)src.at<uchar>(i, j - 1) -
     (float)src.at<uchar>(i, j + 1) - (float)src.at<uchar>(i + 1, j);
   }
   else {//边缘赋值
    a = src.at<uchar>(i, j);
   }
   if (a > 255 || a < 0) {
    dst.at<uchar>(i, j) = src.at<uchar>(i, j);
   }
   else {
    dst.at<uchar>(i, j) = a;
   }
  }
}

// 基本全局阈值方法函数
int BasicGlobalThreshold(Mat src, float oldValue)
{
 int cols = src.cols;
 int rows = src.rows;
 float G1 = 0;
 float G2 = 0;
 float g1 = 0;
 float g2 = 0;
 float u1 = 0;
 float u2 = 0;
 float T0 = 0;
 // 计算灰度直方图分布,统计像素数和频率
 for (int i = 0; i < rows; i++)
 {
  for (int j = 0; j < cols; j++)
  {
   if (src.at<uchar>(i, j) > oldValue)
   {
    G1 += src.at<uchar>(i, j);
    g1 += 1;
   }
   else
   {
    G2 += src.at<uchar>(i, j);
    g2 += 1;
   }
  }
 }
 u1 = G1 / g1;
 u2 = G2 / g2;
 T0 = (u1 + u2) / 2;
 std::cout << T0 << std::endl;
 if (abs(oldValue - T0) < 0.1) {
  return T0;
 }
 else
 {
  BasicGlobalThreshold(src, T0);
 }
}

// Otsu方法函数
int Otsu(Mat src)
{
 int cols = src.cols;
 int rows = src.rows;
 int nPixelNum = cols * rows;
 // 初始化
 int pixelNum[256];
 double probability[256];
 for (int i = 0; i < 256; i++)
 {
  pixelNum[i] = 0;
  probability[i] = 0.0;
 }
 // 统计像素数和频率
 for (int i = 0; i < rows; i++)
 {
  for (int j = 0; j < cols; j++)
  {
   pixelNum[src.at<uchar>(i, j)]++;
  }
 }
 for (int i = 0; i < 256; i++)
 {
  probability[i] = (double)0.1*pixelNum[i] / nPixelNum;
 }
 // 计算
 int Threshold = 0;          // 最佳阈值
 double MaxDelta = 0.0;      // 最大类间方差
 double Mean_0 = 0.0;        // 左边平均值
 double Mean_1 = 0.0;        // 右边平均值
 double Delta = 0.0;         // 类间方差
 double Mean_0_temp = 0.0;   // 左边平均值中间值
 double Mean_1_temp = 0.0;   // 右边平均值中间值
 double Probability_0 = 0.0;       // 左边频率值
 double Probability_1 = 0.0;       // 右边频率值
 for (int j = 0; j < 256; j++)
 {
  for (int i = 0; i < 256; i++)
  {
   if (i < j)// 前半部分
   {
    Probability_0 += probability[i];
    Mean_0_temp += i * probability[i];
   }
   else      // 后半部分
   {
    Probability_1 += probability[i];
    Mean_1_temp += i * probability[i];
   }
  }
  // 计算平均值
  // Mean_0_teamp计算的是前半部分的灰度值的总和除以总像素数,
  // 所以要除以前半部分的频率才是前半部分的平均值,后半部分同样
  Mean_0 = Mean_0_temp / Probability_0;
  Mean_1 = Mean_1_temp / Probability_1;
  Delta = (double)(Probability_0 * Probability_1 * pow((Mean_0 - Mean_1), 2));
  if (Delta > MaxDelta)
  {
   MaxDelta = Delta;
   Threshold = j;
  }
  // 相关参数归零
  Probability_0 = 0.0;
  Probability_1 = 0.0;
  Mean_0_temp = 0.0;
  Mean_1_temp = 0.0;
  Mean_0 = 0.0;
  Mean_1 = 0.0;
  Delta = 0.0;
 }
 return Threshold;
}

void main() {
 Mat image = imread("A1.bmp", 0);
 Mat image1,image2;
 Mat image3(image.size(), image.type());
 Mat image4(image.size(), image.type());

 std::cout << "基本全局阈值方法" << std::endl;

 int OstuThreshold1 = BasicGlobalThreshold(image, 0.01);
 int OstuThreshold2 = Otsu(image);

 std::cout << "Otsu方法" << std::endl;
 std::cout << OstuThreshold2 << std::endl;
 threshold(image, image1, OstuThreshold1, 255, CV_THRESH_OTSU);
 threshold(image, image2, OstuThreshold2, 255, CV_THRESH_OTSU);

 LaplacianSharpDeal(image2, image3);
 LaplacianSharpDeal(image, image4);

 imshow("基本全局阈值方法", image1);
 imshow("Otsu方法", image2);
 imshow("先阈值分割后边缘检测", image3);
 imshow("直接对图像进行边缘检测", image4);
 waitKey();
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C++中实现OpenCV图像分割与分水岭算法

    分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征. API介绍 void watershed( InputArray image, InputOutputArray markers ); 参数说明: image: 必须是一个8bit 3通道彩色图像矩阵序列 markers: 在执行分水岭函数watershed之前,必须对第二个参数markers

  • 基于OpenCV实现图像分割

    本文实例为大家分享了基于OpenCV实现图像分割的具体代码,供大家参考,具体内容如下 1.图像阈值化 源代码: #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> using namespace std; using namespace cv; int thresholds=50; int model=2; Ma

  • python用opencv完成图像分割并进行目标物的提取

    运行平台: Windows Python版本: Python3.x IDE: Spyder 今天我们想实现的功能是对单个目标图片的提取如图所示: 图片读取 ###############头文件 import matplotlib.pyplot as plt import os import cv2 import numpy as np from PIL import Image #from skimage import io import random from PIL import Image

  • Opencv实现用于图像分割分水岭算法

    目标 • 使用分水岭算法基于掩模的图像分割 • 学习函数: cv2.watershed() 原理   任何一幅灰度图像都可以被看成拓扑平面,灰度值高的区域可以被看成是山峰,灰度值低的区域可以被看成是山谷.我们向每一个山谷中灌不同颜色的水,随着水的位的升高,不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合,我们需要在水汇合的地方构建起堤坝.不停的灌水,不停的构建堤坝直到所有的山峰都被水淹没.我们构建好的堤坝就是对图像的分割.这就是分水岭算法的背后哲理.   但是这种方法通常都会得到过度分割的结果

  • Python使用OpenCV和K-Means聚类对毕业照进行图像分割

    图像分割是将图像分割成多个不同区域(或片段)的过程.目标是将图像的表示变成更容易和更有意义的图像. 在这篇博客中,我们将看到一种图像分割方法,即K-Means Clustering. K-Means 聚类是一种无监督机器学习算法,旨在将N 个观测值划分为K 个聚类,其中每个观测值都属于具有最近均值的聚类.集群是指由于某些相似性而聚合在一起的数据点的集合.对于图像分割,这里的簇是不同的图像颜色. 我们使用的环境是pip install opencv-python numpy matplotlib

  • OpenCV图像分割中的分水岭算法原理与应用详解

    图像分割是按照一定的原则,将一幅图像分为若干个互不相交的小局域的过程,它是图像处理中最为基础的研究领域之一.目前有很多图像分割方法,其中分水岭算法是一种基于区域的图像分割算法,分水岭算法因实现方便,已经在医疗图像,模式识别等领域得到了广泛的应用. 1.传统分水岭算法基本原理 分水岭比较经典的计算方法是L.Vincent于1991年在PAMI上提出的[1].传统的分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一像素的灰度值表示该点的海

  • python+opencv图像分割实现分割不规则ROI区域方法汇总

    在图像分割领域,一个重要任务便是分割出感兴趣(ROI)区域.如果是简易的矩形ROI区域其实是非常容易分割的,opencv的官方python教程里也有教到最简易的矩形ROI分割(剪裁),其本质是多维数组(矩阵)的切片.但是现实情况中,ROI是不规则的多边形,也可能是曲线边界,那么该如何分割出来呢?下面总结几种思路. 可能只提供核心部分的代码示例,具体应用要结合你自己的项目来修正. 一.已知边界坐标,直接画出多边形 例:最基础的画个四边形 # 定义四个顶点坐标 pts = np.array([[10

  • openCV实现图像分割

    本次实验为大家分享了openCV实现图像分割的具体实现代码,供大家参考,具体内容如下 一.实验目的 进一步理解图像的阈值分割方法和边缘检测方法的原理. 掌握图像基本全局阈值方法和最大类间方差法(otsu法)的原理并编程实现. 编程实现图像的边缘检测. 二.实验内容和要求 编程实现图像阈值分割(基本全局阈值方法和otsu法)和边缘检测. 三.实验主要仪器设备和材料 计算机,VS2017+OpenCV 四.实验原理与方法 图像的阈值分割的基本原理 图像的二值化处理图像分割中的一个主要内容,就是将图像

  • Python 深入了解opencv图像分割算法

    使用 OpenCV 函数 cv::filter2D 执行一些拉普拉斯滤波以进行图像锐化 使用 OpenCV 函数 cv::distanceTransform 以获得二值图像的派生(derived)表示,其中每个像素的值被替换为其到最近背景像素的距离 使用 OpenCV 函数 cv::watershed 将图像中的对象与背景隔离 加载源图像并检查它是否加载没有任何问题,然后显示它: # Load the image parser = argparse.ArgumentParser(descript

  • OpenCV图像分割之分水岭算法与图像金字塔算法详解

    目录 前言 一.使用分水岭算法分割图像 1.cv2.distanceTransform()函数 2.cv2.connectedComponents()函数 3.cv2.watershed()函数 二.图像金字塔 1.高斯金字塔向下采样 2.高斯金字塔向上采样 3.拉普拉斯金字塔 4.应用图像金字塔实现图像的分割和融合 前言 主要介绍OpenCV中的分水岭算法.图像金字塔对图像进行分割的方法. 一.使用分水岭算法分割图像 分水岭算法的基本原理为:将任意的灰度图像视为地形图表面,其中灰度值高的部分表

随机推荐