灰度化、二值化、边缘检测、轮廓检测

灰度化

定义

灰度图像是只含亮度信息,不含色彩信息的图像。灰度化处理是把彩色图像转换为灰度图像的过程,是图像处理中的基本操作。OpenCV 中彩色图像使用 BGR 格式。灰度图像中用 8bit 数字 0~255 表示灰度,如:0 表示纯黑,255 表示纯白。

函数讲解

  1. 使用 cvtColor 函数将图像转换为灰度图像:
import cv2
img = cv2.imread('image.jpg')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray Image', gray_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
  1. 使用 cv2.imread 函数读取图像,并使用 cv2.cvtColor 函数将图像转换为灰度图像:
import cv2
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imshow('Gray Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

二值化

定义

图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。其实图像二值化的方法有不下二十种。一般分为Global和Local两类,区别就是寻找那个阈值的时候使用到了哪里的信息。

但是单一的图像二值化方法(指基于直方图的二值化)往往比不过其他的方法,因为,毕竟你信息丢了太多。但是二值化快啊……你可以进行一百次不同的二值化,然后再得到一个更好的结果……

全局阈值法

简单阈值是选取一个全局阈值,然后把整幅图像分成非黑即白的二值图像,灰度值大于阈值就赋为255反之为0。

特点:对整幅图像都是用一个统一的阈值来进行二值化,方法简单易于实现。但是对光照不均匀的图像容易出现错误的二值分割。

cv::threshold(graySrc,dst, threshold_value, threshold_max,THRESH_BINARY);

自适应阈值法

自适应阈值法的原理就是将像素点与该点所在区域的像素的平均值(最大值、中位数等)做比较,大于则赋予255;反之,为0。

特点:在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。

cv.adaptiveThreshold(src,dst,maxValue,adaptiveMethod,blockvalue,C)
  • src表示需要进行二值化的图像;需要注意的是,该输入必须是8-bit单通道的图像;
  • dst表示输出图像的二值图像;
  • maxValue是一个非零值,用于对哪些满足条件的阈值进行赋值;
  • adaptiveMethod表示选择哪一种自适应阈值算法;Opencv提供两种,ADAPTIVE_THRESH_MEAN_C(领域内均值)与ADAPTIVE_THRESH_GAUSSIAN_C(领域内像素点加权和)
  • blockvalue表示设定方阵的大小,将一个点与其周围的方阵数据对比,合适的方阵大小对于结果的影像较大。
  • 常数,每个区域计算出的阈值的基础上在减去这个常数作为这个区域的最终阈值,可以为负数。

原文链接:https://blog.csdn.net/weixin_51775350/article/details/128175134

OTSU二值化(最大类间方差法)

Otsu算法(大津法或最大类间方差法)使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小,(相当于类间保持差异性,类内保持一致性)通过方差的计算来寻找一个合适的灰度级别来划分。 所以可以在二值化的时候采用otsu算法来自动选取阈值进行二值化。otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。

大津算法的核心思想是通过寻找一个阈值,将图像的像素分为两个类别:前景和背景。具体步骤如下:

  • 统计图像的灰度直方图,得到每个灰度级的像素数目。
  • 遍历所有可能的阈值(0到255),计算根据该阈值将图像分为前景和背景的类内方差。
  • 根据类内方差的最小值确定最佳阈值。

特点:类间方差法对噪音和目标大小十分敏感,它仅对类间方差为单峰的图像产生较好的分割效果。当目标与背景的大小比例悬殊时,类间方差准则函数可能呈现双峰或多峰,此时效果不好,但是类间方差法是用时最少的。

cv::threshold(gray, img_Thr_O, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); 
// *第一个参数:待二值化的图像,图像只能是CV_8U和CV_32F两种类型。对于
// 图像通道数目的要求与选择二值化的方法有关。
// *第二个参数:二值化后的图像
// *第三个参数:二值化阈值
// *第四个参数:二值化过程的最大值,它只在THRESH_BINARY和THRESH_BINARY_INV
// 两种方法中才使用。

方差(Variance)是用来衡量一组数据离其平均值的分散程度的统计量。这组数据的每个数值与其平均值之间的差的平方的平均值就是这组数据的方差。数学上的定义如下:
设有一组数据X={x1, x2, …, xn},其中n是数据量,其平均值为μ,那么方差σ2的计算公式为:σ2 = (1/n) * Σ(xi - μ)^2 , Σ代表所有数据的累加。

方差的计算描述了数据数值与其平均值之间的差异性,方差越大,数据的分散程度越大,反之则数据更集中。

边缘检测

定义

边缘检测是图像处理中非常重要的一项任务,它的目标是找到图像中亮度变化明显的区域,这些区域通常对应物理世界中的物体边缘。边缘检测常用于图像分割和特征提取。

图像的边缘检测大幅度减少数据量,并剔除了可以认为不相关的信息,保留了图像重要的结构属性。有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于搜索和基于零穿越。

  • 基于搜索:通过寻找图像一阶导数中的最大值来检测边界,然后利用计算结果估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法是Sobel算子和Scharr算子。
  • 基于零穿越:通过寻找图像二阶导数零穿越来寻找边界,代表算法是Laplacian算子。

备注:sobel算子时基于图像的空间强度变化,对应在水平和垂直方向的差异性,即像素的强度在边缘地方有显著变化。
Sobel算子的一种形式在水平方向(dx)的核是这样的:

-1  0  1
-2  0  2
-1  0  1

这个核的设计是为了计算图像在水平方向上的梯度,并提取出边缘。核中的每一个元素对应一个权重,用于对其覆盖的像素区域加权求和。
中间的列(0, 0, 0)给予了原始位置的像素0的权重,这意味着原始位置的像素对结果没有影响。
左边和右边的列(-1, -2, -1)和(1, 2, 1)则对原始像素的左右两旁的像素赋予了权重,左边是负数,右边是正数。这代表我们希望检测从暗到亮(左到右)的边缘。

轮廓检测

定义

轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或灰度,提取轮廓就是提取这些具有相同颜色或者灰度的曲线,或者说是连通域,轮廓在形状分析和物体检测和识别中非常有用。

函数讲解

cv2.findContours() 是一个在OpenCV库中的函数,它用于从二值图像中检测轮廓。
函数的定义如下:

参数说明:
cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
  • image: 必须是8位单通道二值图像。在运行此函数后,原图像将被修改。注意通常需要使用阈值处理或边缘检测之后的结果图像。
  • mode: 此参数定义了轮廓检测模式。例如,cv2.RETR_TREE会检测所有轮廓并创建完整的轮廓层级系列。 cv2.RETR_EXTERNAL则只检测最外面的轮廓。
  • method: 此参数定义了轮廓的近似方法。例如,cv2.CHAIN_APPROX_SIMPLE将存储轮廓线段的结束点,而cv2.CHAIN_APPROX_NONE则存储轮廓上的所有点。

函数返回值:

  • contours: 这是一个Python列表,其中存储了图像中所有轮廓。每一条轮廓都是一个包含轮廓上各点(x, y)坐标的NumPy数组。
  • hierarchy: (仅当mode参数为cv2.RETR_CCOMP或cv2.RETR_TREE时存在) 它是一个包含有关图像拓扑信息的NumPy数组。

例子代码

以下python代码会找到并画出图像中的所有外部轮廓:

import cv2
import numpy as np

# 加载图像
image = cv2.imread('image.png', cv2.IMREAD_GRAYSCALE)

# 二值化图像
_, thresholded = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)

# 寻找轮廓
contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 在原图上画出轮廓
cv2.drawContours(image, contours, -1, (0,255,0), 3)

cv2.imshow("Contours", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

你可能感兴趣的:(图像处理,opencv,计算机视觉,人工智能)