import numpy as np
import matplotlib.pyplot as plt
import cv2 as cv
# opencv 默认读取是BGR
mat = cv.imread('man.jpg')
# matplotlib 默认显示是RGB
mat = cv.cvtColor(mat, cv.COLOR_BGR2RGB)
(h, w , c) = mat.shape[0:3]
print('宽:{0}\n高:{1}\n通道:{2}'.format(w, h, c))
plt.imshow(mat)
plt.show()
宽:500
高:503
通道:3
1. Коррозия Эрозия
Принцип заключается в том, чтобы взять локальный минимум на небольшом участке исходного изображения. Это ядро также называют структурирующим элементом, поскольку морфологические операции фактически реализуются сверткой, а структурирующими элементами могут быть прямоугольники, эллипсы и кресты.
# 矩形结构
kernel_rect = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
# print (kernel_rect)
# 椭圆结构
kernel_ellipse = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))
# print (kernel_ellipse)
# 十字结构
kernel_cross = cv.getStructuringElement(cv.MORPH_CROSS, (5, 5))
# print (kernel_cross)
# 用矩形结构进行腐蚀
imgs = [mat]
# matplot 默认是显示RGB
plt.figure(figsize=(12,2))
for i in range(5):
mat_rect = cv.erode(mat, kernel_rect, iterations=i+1)
imgs.append(mat_rect)
plt.subplot(1, 5, i+1)
plt.imshow(imgs[i])
plt.show()
2. Расширение
Расширение противоположно коррозии,
imgs = [mat]
plt.figure(figsize=(12,2))
for i in range(5):
mat_rect = cv.dilate(mat, kernel_rect, iterations=i+1)
imgs.append(mat_rect)
plt.subplot(1, 5, i+1)
plt.imshow(imgs[i])
plt.show()
3. Открытая операция
Сначала коррозия, а затем расширение для удаления белых пятен
# 矩形结构 区域放大些
kernel_rect = cv.getStructuringElement(cv.MORPH_RECT, (10, 10))
opening = cv.morphologyEx(mat, cv.MORPH_OPEN, kernel_rect)
imgs = [mat, opening]
plt.figure(figsize=(6,2))
for i in range(2):
plt.subplot(1, 2, i+1)
plt.imshow(imgs[i])
plt.show()
В-четвертых, закрытая операция
Вздутие сначала в коррозии, удаление черных точек
# 矩形结构
kernel_rect = cv.getStructuringElement(cv.MORPH_RECT, (4, 4))
closing = cv.morphologyEx(mat, cv.MORPH_CLOSE, kernel_rect)
imgs = [mat, closing]
plt.figure(figsize=(6,2))
for i in range(2):
plt.subplot(1, 2, i+1)
plt.imshow(imgs[i])
plt.show()
5. Первая операция открытия - это операция закрытия.
kernel_rect = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
opening = cv.morphologyEx(mat, cv.MORPH_OPEN, kernel_rect)
closing = cv.morphologyEx(opening, cv.MORPH_CLOSE, kernel_rect)
imgs = [mat, opening, closing]
plt.figure(figsize=(8,2))
for i in range(3):
plt.subplot(1, 3, i+1)
plt.imshow(imgs[i])
plt.show()
6. Грандиентный морфологический градиент
Карта расширения вычитается из карты эрозии, чтобы получить контур изображения.
kernel_rect = cv.getStructuringElement(cv.MORPH_RECT, (4, 4))
grandient = cv.morphologyEx(mat, cv.MORPH_GRADIENT, kernel_rect)
imgs = [mat, grandient]
plt.figure(figsize=(6,2))
for i in range(2):
plt.subplot(1, 2, i+1)
plt.imshow(imgs[i])
plt.show()
7. Белая шляпа
Исходное изображение минус открытое операционное изображение. То есть получаются удаленные белые точки.
kernel_rect = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
topHat = cv.morphologyEx(mat, cv.MORPH_TOPHAT, kernel_rect)
imgs = [mat, topHat]
plt.figure(figsize=(6,2))
for i in range(2):
plt.subplot(1, 2, i+1)
plt.imshow(imgs[i])
plt.show()
Восемь, черная шляпа
Исходное изображение минус закрытая операция. То есть получить удаленную черную точку
kernel_rect = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
blackHat = cv.morphologyEx(mat, cv.MORPH_BLACKHAT, kernel_rect)
imgs = [mat, blackHat]
plt.figure(figsize=(6,2))
for i in range(2):
plt.subplot(1, 2, i+1)
plt.imshow(imgs[i])
plt.show()
9. Сглаживание изображения
- Средний Вычислите среднее значение всех пикселей в области, покрытой полем свертки, чтобы получить результат свертки.
# opencv 默认读取是BGR
mat = cv.imread('face.jpeg')
# matplotlib 默认显示是RGB
mat = cv.cvtColor(mat, cv.COLOR_BGR2RGB)
kernelsizes = [(3, 3), (15, 15), (35, 35)]
plt.figure(figsize=(15, 15))
plt.subplot(1, 4, 1)
plt.imshow(mat)
for i, kernel in enumerate(kernelsizes):
plt.subplot(1, 4, i+2)
# 平均平滑
blur = cv.blur(mat, kernel)
plt.imshow(blur)
plt.show()
- Размытие по Гауссу Теперь замените ядро свертки ядром Гаусса, ядро соответствует гауссовскому распределению, значение центра поля является наибольшим, а остальные поля уменьшаются в соответствии с расстоянием от центрального элемента. Составьте набор холмов Гаусса и найдите средневзвешенное значение.
kernelsizes = [(3, 3), (15, 15), (35, 35)]
plt.figure(figsize=(15, 15))
plt.subplot(1, 4, 1)
plt.imshow(mat)
for i, kernel in enumerate(kernelsizes):
plt.subplot(1, 4, i+2)
# 高斯模糊
blur = cv.GaussianBlur(mat, kernel, 5)
plt.imshow(blur)
plt.show()
- Среднее срединное размытие Среднее значение, соответствующее блоку свертки, используется для замены значения центрального пикселя.
plt.figure(figsize=(15, 15))
plt.subplot(1, 4, 1)
plt.imshow(mat)
for i, kernel in enumerate((3, 15, 35)):
plt.subplot(1, 4, i+2)
# 中值模糊
blur = cv.medianBlur(mat, kernel, 5)
plt.imshow(blur)
plt.show()
- Двусторонняя двусторонняя фильтрация
Он может эффективно удалять шум, сохраняя при этом четкость границ. Учитывается не только пространственное отношение между точками пикселей, но и отношение между значениями пикселей (сходство пикселей).
# 定义三组数据 diameter 定义半径
params = [(11, 20, 7), (15, 40, 30), (40, 40, 30)]
plt.figure(figsize=(15, 15))
plt.subplot(1, 4, 1)
plt.imshow(mat)
# 领域直径,灰度值相似性高斯函数标准差,空间高斯函数标准差
for i, (diameter, sigmaColor, sigmaSpace) in enumerate(params):
print('{0},{1},{2}'.format(diameter, sigmaColor, sigmaSpace))
plt.subplot(1, 4, i+2)
# 双边滤波
blur = cv.bilateralFilter(mat, diameter, sigmaColor, sigmaSpace)
plt.imshow(blur)
plt.show()
11,20,7
15,40,30
40,40,30
10. Бинаризация изображения
-
THRESH_BINARY
Больше порога становится максимальным значением Меньше порога становится 0
-
THRESH_BINARY_INV
Напротив THRESH_BINARY
-
THRESH_TRUNC
Если он больше порога, то станет равным порогу, а если меньше порога, останется неизменным.
-
THRESH_TOZERO
Если больше порога, то становится 0, а если меньше порога, то не меняется
-
THRESH_TOZERO_INV
Больше порога, без изменений, меньше порога становится 0
mat = cv.imread('money.jpg')
mat = cv.cvtColor(mat, cv.COLOR_BGR2RGB)
plt.figure(figsize=(15, 15))
plt.subplot(1, 7, 1)
plt.title('original')
plt.imshow(mat)
gray = cv.cvtColor(mat, cv.COLOR_RGB2GRAY)
plt.subplot(1, 7, 2)
plt.title('gray')
plt.imshow(gray, cmap='gray')
ret = 127
# 127 阈值, 255 最大值
ret1,thresh1 = cv.threshold(gray, ret, 255, cv.THRESH_BINARY)
ret2,thresh2 = cv.threshold(gray, ret, 255, cv.THRESH_BINARY_INV)
ret3,thresh3 = cv.threshold(gray, ret, 255, cv.THRESH_TRUNC)
ret4,thresh4 = cv.threshold(gray, ret, 255, cv.THRESH_TOZERO)
ret5,thresh5 = cv.threshold(gray, ret, 255, cv.THRESH_TOZERO_INV)
titles = ['THRESH_BINARY', 'THRESH_BINARY_INV', 'THRESH_TRUNC', 'THRESH_TOZERO', 'THRESH_TOZERO_INV']
imgs = [thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(5):
plt.subplot(1, 7 , i+3)
plt.title(titles[i])
plt.imshow(imgs[i],cmap='gray')
plt.show()
- Автоматический выбор порогов
gray = cv.cvtColor(mat, cv.COLOR_RGB2GRAY)
ret1,thresh1 = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
print('阈值:{0}'.format(ret1))
ret2,thresh2 = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
print('阈值:{0}'.format(ret2))
阈值:133.0
阈值:133.0
自适应阈值
前面的部分我们使用全局阈值,但是当图像上的不同部分具体不同亮度时候效果就不是很好。这种情况下我们需要采用自适应阈值,
В это время пороговое значение вычисляется в соответствии с каждой небольшой областью на изображении, и вычисляется соответствующее пороговое значение. Поэтому в разных областях одного и того же изображения используются разные пороги.
Aapative Method — определяет метод вычисления порога:
-
ADPTIVE_THRESH_MEAN_C
Порог берется из среднего значения соседних регионов.
-
ADPTIVE_THRESH_GAUSSIAN_C
Порог представляет собой взвешенную сумму смежных областей, а вес представляет собой окно Гаусса.
Другие параметры:
-
Размер блока - размер смежного блока (используется для расчета размера пороговой области)
-
C Это константа, а порог равен среднему или средневзвешенному значению минус эта константа.
plt.figure(figsize=(15, 8))
plt.subplot(2, 3, 1)
plt.title('original')
plt.imshow(mat)
plt.subplot(2, 3, 2)
plt.title('gray')
plt.imshow(gray, cmap='gray')
# 中值滤播
median = cv.medianBlur(gray, 5)
plt.subplot(2, 3, 3)
plt.title('median')
plt.imshow(median, cmap='gray')
# 普通二值化
ret1,th1 = cv.threshold(median, 127, 255, cv.THRESH_BINARY)
plt.subplot(2, 3, 4)
plt.title('THRESH_BINARY_N')
plt.imshow(th1, cmap='gray')
# 平均值阈值
th2 = cv.adaptiveThreshold(median, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 5, 3)
plt.subplot(2, 3, 5)
plt.title('ADAPTIVE_THRESH_MEAN_C')
plt.imshow(th2, cmap='gray')
# 高斯阈值
th3 = cv.adaptiveThreshold(median, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 5, 2)
plt.subplot(2, 3, 6)
plt.title('ADAPTIVE_THRESH_GAUSSIAN_C')
plt.imshow(th3, cmap='gray')
plt.show()
Одиннадцать, цветовое пространство изображения
- RGB
mat = cv.imread('man.jpg')
mat = cv.cvtColor(mat, cv.COLOR_BGR2RGB)
(R, G, B) = cv.split(mat)
zeros = np.zeros(mat.shape[:2], dtype='uint8')
plt.figure(figsize=(15, 15))
plt.subplot(1, 4, 1)
plt.imshow(mat)
for i in range(3):
plt.subplot(1, 4, i+2)
# 平均平滑
merge = cv.merge([R if i==0 else zeros, G if i==1 else zeros, B if i==2 else zeros])
plt.imshow(merge)
plt.show()
- ВПГ HSV — это более интуитивно понятное цветовое представление. Цветовое пространство HSV позволяет лучше обрабатывать цвета в цифровом виде. Параметры цвета в этой модели: Оттенок (H, Hue) Диапазон: 0–360 (0: красный) (60: желтый) (120: зеленый) (180: голубой) (240: синий) (300: пурпурный) Насыщенность (S, Saturation), степень приближения цвета к спектральному цвету, белая составляющая спектрального цвета равна 0, диапазон 0-100, чем больше значение, тем насыщеннее Яркость (V, значение) указывает на яркость цвета. Значение 0 — черный 100 — белый
hsv = cv.cvtColor(mat, cv.COLOR_RGB2HSV)
zeros = np.zeros(mat.shape[0:2], dtype='uint8')
plt.figure(figsize=(15, 15))
plt.subplot(1, 4, 1)
plt.title('m')
plt.imshow(mat)
i = 2
for (name,chan) in zip(('H', 'S', 'V'), cv.split(hsv)):
global i
plt.subplot(1, 4, i)
i = i + 1
plt.title(name)
plt.imshow(chan)
plt.show()
- Lab
L представляет легкость цвета Положительные значения обозначают красный цвет, отрицательные значения обозначают зеленый цвет. b Положительные значения обозначают желтый цвет, отрицательные значения обозначают синий цвет.
Lab = cv.cvtColor(mat, cv.COLOR_RGB2LAB)
zeros = np.zeros(mat.shape[0:2], dtype='uint8')
plt.figure(figsize=(15, 15))
plt.subplot(1, 4, 1)
plt.title('m')
plt.imshow(mat)
i = 2
for (name,chan) in zip(('L', 'A', 'B'), cv.split(Lab)):
global i
plt.subplot(1, 4, i)
i = i + 1
plt.title(name)
plt.imshow(chan)
plt.show()
- Grayscale
Изображение в градациях серого — это черно-белое изображение, которое отображается путем настройки значения градаций серого.
gray = cv.cvtColor(mat, cv.COLOR_RGB2GRAY)
plt.figure(figsize=(8, 4))
plt.subplot(1, 2, 1)
plt.imshow(mat)
plt.subplot(1, 2, 2)
plt.imshow(gray, cmap='gray')
plt.show()
12. Градиент изображения
позиция в пикселях
x-1,y-1 | x,y-1 | x+1,y-1 |
x-1,y | x,y | x+1,y |
x-1,y+1 | x,y+1 | x+1,y+1 |
I относится к значению пикселя изображения
Первая производная: Градиент xGx = I(x+1,y)-I(x,y)
Градиент y Gy = I(x,y+1) - I(x,y)
Вторая производная: Градиент x: Gx = I(x+1,y)+I(x-1,y) - 2I(x,y)
Градиент y: Gy = I(x,y+1)+I(x,y-1)-2I(x,y)
Величина и направление градиента:
G =
=
Opencv предоставляет три различных градиентных фильтра: Sobel, Scharr, Lapiacian.
mat = cv.imread('car.jpg')
mat = cv.cvtColor(mat, cv.COLOR_BGR2RGB)
gray = cv.cvtColor(mat, cv.COLOR_RGB2GRAY)
# 拉普拉斯算子
# CV_64F 输入出图像的深度(数据类型),64位float类型,因为梯度可能是正也可能是负
laplacian = cv.Laplacian(gray, cv.CV_64F)
# Sobel 算子
# 1,0 表示在x方向求一阶导数,最大可以求2阶导数
sobelx = cv.Sobel(gray,cv.CV_64F,1, 0, ksize=3)
# 0,1 表示在y方向求一阶导数,最大可以求2阶导数
sobely = cv.Sobel(gray,cv.CV_64F,0, 1, ksize=3)
imgs = [mat, gray, laplacian, sobelx, sobely]
titles = ['Original', 'gray', 'laplacian', 'sobelx', 'sobely']
plt.figure(figsize=(15, 5))
for i in range(5):
plt.subplot(1, 5, i+1)
plt.imshow(imgs[i], cmap='gray')
plt.title(titles[i])
plt.show()
Тринадцать, обнаружение краев Канни
1. Удаление шума
由于边缘检测很容易受到噪声影响,所以第一步是使用高斯滤波器去除噪声。
2. Рассчитайте градиент изображения
对平滑后的图像使用Sobel算子计算水平方向和垂直方向的梯度。
3. Немаксимальное подавление
在获取梯度方向和大小后,应该对整幅图像做个扫描,去除那些非边界上的点,对每个像素进行检查,在看这个点的梯度是不是周围具有相同梯度方向的点中最大的。
4. Порог гистерезиса
现在要确定那些边界才是真正的边界。这时我们需要设定两个阈值:minValue 和 maxValue,即高于maxVlue 被认为是边界,小于minValue 抛弃。
# Canny 函数
# 参数一 输入图像
# 参数二 minVal
# 参数三 maxValue
# 参数四 Sobel算子 卷积核大小
# 参数五 设定求梯度大小的公式
edges = cv.Canny(gray, 50, 150)
imgs = [mat, edges]
titles = ['Original', 'Canny']
plt.figure(figsize=(16, 16))
for i in range(2):
plt.subplot(1, 2, i+1)
plt.imshow(imgs[i], cmap='gray')
plt.title(titles[i])
plt.show()
# 自动确定阈值的方法
def auto_canny(image, sigma=0.33):
v = np.median(image)
lower = int(max(0, (1-sigma)*v))
upper = int(min(255, (1+sigma)*v))
return (lower, upper)
(minValue, maxValue) = auto_canny(gray, sigma=0.1)
print('minValue:{0},maxValue:{1}'.format(minValue, maxValue))
edges = cv.Canny(gray, minValue, maxValue)
imgs = [mat, edges]
titles = ['Original', 'Canny']
plt.figure(figsize=(16, 16))
for i in range(2):
plt.subplot(1, 2, i+1)
plt.imshow(imgs[i], cmap='gray')
plt.title(titles[i])
plt.show()
print(edges)
minValue:207,maxValue:254