opencv
文章目录
图像基本操作
import numpy as np
import cv2 as cv
# 读取图片并显示
original = cv.imread('data/forest.jpg')
cv.imshow('Original', original)
# 显示图片某个颜色通道的图像
blue = np.zeros_like(original)
blue[:, :, 0] = original[:, :, 0] # 0 - 蓝色通道
cv.imshow('Blue', blue)
green = np.zeros_like(original)
green[:, :, 1] = original[:, :, 1] # 1 - 绿色通道
cv.imshow('Green', green)
red = np.zeros_like(original)
red[:, :, 2] = original[:, :, 2] # 2 - 红色通道
cv.imshow('Red', red)
# 图像裁剪
h, w = original.shape[:2]
l, t = int(w / 4), int(h / 4)
r, b = int(w * 3 / 4), int(h * 3 / 4)
cropped = original[t:b, l:r]
cv.imshow('Cropped', cropped)
#图像缩放 interpolation=线型插值
scaled1 = cv.resize(original, (int(w / 4), int(h / 4)),
interpolation=cv.INTER_LINEAR)
cv.imshow('Scaled1', scaled1)
scaled2 = cv.resize(
scaled1, None, fx=4, fy=4,
interpolation=cv.INTER_LINEAR)
cv.imshow('Scaled2', scaled2)
cv.waitKey()
# 图像文件保存
cv.imwrite('data/blue.jpg', blue)
图像平滑处理
均值滤波
均值滤波是典型的线性滤波算法。
它是指在图像上由一个归一化卷积框针对目标全体像素执行卷积运算,使用均值化后的结果来代替原来的像素值。
均值滤波本身存在着固有的缺陷,即它不能很好地保护图像细节,在图像去噪的同时也破坏了图像的细节部分,从而使图像变得模糊,不能很好地去除噪声点。
def cv_blur(img):
# img: 原始图像
# (5, 5): 卷积窗大小
dst = cv.blur(img, (5, 5))
return dst
img = cv.imread('data/miantiao.jpg')
cv.imshow('original', img)
高斯滤波
高斯滤波是一种线性平滑滤波,适用于消除高斯噪声。
高斯滤波将计算每一个像素点的值,该值由其本身和邻域内的其他像素值经过高斯函数加权平均后得到。
高斯滤波后图像被平滑的程度取决于标准差。离中心越近的像素权重越高。因此,相对于均值滤波(mean filter)它的平滑效果更柔和,而且边缘保留的也更好。
def cv_gaussian_blur(img):
# 0,0 : 分别表示x方向标准差与y方向标准差。若为0则代表按照内核大小自动适配。
dst = cv.GaussianBlur(img, (5, 5), 0, 0)
return dst
dst2 = cv_gaussian_blur(img)
cv.imshow('gaussian blur', dst2)
中值滤波
中值滤波法是一种非线性平滑算法,它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值。
中值滤波模板就是用卷积框中像素的中值代替中心值,达到去噪声的目的。
中值滤波一般用于去除椒盐噪声,在图像中,它是一种随机出现的白点或者黑点,可能是亮的区域有黑色像素或是在暗的区域有白色像素(或是两者皆有)。
def cv_median_blur(img):
#5: 内核大小 (只需一个值,因为我们使用正方形窗口),必须为奇数。
dst = cv.medianBlur(img, 5)
return dst
median_src = cv.imread('data/median.jpg')
cv.imshow('median original', median_src)
dst3 = cv_median_blur(median_src)
cv.imshow('median blur', dst3)
双边滤波
双边滤波是一种非线性滤波器,它可以达到保持边缘、降噪平滑的效果。
基于高斯分布,用周边像素亮度值的加权平均代表某个像素的强度。
之所以能够达到保边去噪的滤波效果是因为滤波器由两个函数构成:一个函数是由几何空间距离决定滤波器系数,另一个是由像素差值决定滤波器系数。
高斯滤波器只考虑像素之间的空间关系,而不会考虑像素值之间的关系(像素的相似度)。所以这种方法不会考虑一个像素是否位于边界。因此边界也会别模糊掉,而这正不是我们想要 。
双边滤波在同时使用空间高斯权重和灰度值相似性高斯权重。空间高斯函数确保只有邻近区域的像素对中心点有影响,灰度值相似性高斯函数确保只有与中心像素灰度值相近的才会被用来做模糊运算。所以这种方法会确保边界不会被模糊掉,因为边界处的灰度值变化比较大。
def cv_bilateral_blur(img):
# 50: 邻域直径
# 40: 颜色标准差,颜色标准差越大,表示更大范围的颜色会被混合在一起。
# 5: 空间标准差,空间标准差越大,模糊范围更大。但如果没有达到颜色标准差的标准则会保留边界。
dst = cv.bilateralFilter(img, 50, 40, 5)
return dst
bilateral_src = cv.imread('data/qtx_.jpg')
cv.imshow('bilateral original', bilateral_src)
dst3 = cv_bilateral_blur(bilateral_src)
cv.imshow('bilateral blur', dst3)
图像阈值处理
图像阈值处理是最简单的图像分割的方法。
应用举例:从一副图像中利用阈值分割出我们需要的物体部分(当然这里的物体可以是一部分或者整体)。这样的图像分割方法是基于图像中物体与背景之间的灰度差异,而且此分割属于像素级的分割。
为了从一副图像中提取出我们需要的部分,应该用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断。(注意:阈值的选取依赖于具体的问题。即:物体在不同的图像中有可能会有不同的灰度值。
一旦找到了需要分割的物体的像素点,我们可以对这些像素点设定一些特定的值来表示。(例如:可以将该物体的像素点的灰度值设定为:‘0’(黑色),其他的像素点的灰度值为:‘255’(白色);当然像素点的灰度值可以任意,但最好设定的两种颜色对比度较强,方便观察结果)。
简单阈值化处理
简单阈值化处理先要选定一个特定的阈值量,比如:125,这样,大于125的像素点与小于125的像素点分别设置新的不同的灰度值。
def simple_thresholding(img): #简单阈值化
# 设置127为阈值,大于127的像素点的灰度值设定为255,灰度值小于125的像素点的灰度值设定为0。
ret, thresh1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
# 设置127为阈值,大于127的像素点的灰度值设定为0,灰度值小于125的像素点的灰度值设定为255。
ret, thresh2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)
# 设置127为阈值,大于127的像素点的灰度值设定为255,灰度值小于127的像素点的灰度值不变。
ret, thresh3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC)
# 设置127为阈值,大于127的像素点的灰度值不变,灰度值小于127的像素点的灰度值设置为0。
ret, thresh4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO)
# 设置127为阈值,大于127的像素点的灰度值设置为0,灰度值小于125的像素点的灰度值不变。
ret, thresh5 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
return titles, images
# 测试
img = cv.cvtColor(cv.imread('data/chepai.jpg'), cv.COLOR_BGR2GRAY)
titles, imgs = adaptive_thresholding(img)
for title, img in zip(titles, imgs):
cv.imshow(title, img)
cv.waitKey()
自适应阈值化处理
在上一节中,我们使用一个全局值作为阈值。但这在某些情况下效果不好。例如,如果图像在不同区域具有不同的照明条件时。在这种情况下,自适应阈值化可以帮助。算法根据像素周围的一个小区域来确定像素的阈值。因此,对于同一幅图像的不同区域,我们得到了不同的阈值,对于不同光照的图像,得到了更好的结果。
def adaptive_thresholding(img):
img = cv.medianBlur(img, 3)
ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
# th1: 输入图像
# 255: 超过计算所得阈值时设置为255
# 参数常量可设置如下:
# cv.ADAPTIVE_THRESH_MEAN_C:阈值是邻域面积的平均值减去常数C。
# cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值减去常数C的高斯加权和。
# 11: 窗口大小
# 2: 常数C
th2 = cv.adaptiveThreshold(th1, 255, cv.ADAPTIVE_THRESH_MEAN_C, \
cv.THRESH_BINARY, 11, 2)
th3 = cv.adaptiveThreshold(th1, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, \
cv.THRESH_BINARY, 11, 2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
return titles, images
if __name__ == '__main__':
img = cv.cvtColor(cv.imread('data/chepai.jpg'), cv.COLOR_BGR2GRAY)
titles, imgs = adaptive_thresholding(img)
for title, img in zip(titles, imgs):
cv.imshow(title, img)
cv.waitKey()
图像形态学处理
腐蚀(erosion)
original = cv.imread('data/let.png')
original = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
cv.imshow('original', original)
# 定义核元素
kernel = np.ones((5, 5), np.uint8)
# 腐蚀
erosion = cv.erode(original, kernel, iterations=1)
cv.imshow('erosion', erosion)
膨胀(dilation)
# 膨胀
dilation = cv.dilate(original, kernel, iterations = 1)
cv.imshow('dilation', dilation)
开运算(MORPH_OPEN)与闭运算(MORPH_CLOSE)
开运算:先腐蚀后膨胀,用于移除由图像噪音形成的斑点。
闭运算:先膨胀后腐蚀,用来连接被误分为许多小块的对象;
# 开运算 先腐蚀后膨胀
original = cv.imread('data/opening.png')
original = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
opening = cv.morphologyEx(original, cv.MORPH_OPEN, kernel)
cv.imshow('opening1', np.hstack((original, opening)))
# 闭运算 先膨胀后腐蚀
original = cv.imread('data/closing.png')
original = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
closing = cv.morphologyEx(original, cv.MORPH_CLOSE, kernel)
cv.imshow('closing', np.hstack((original, closing)))
图像轮廓处理
https://www.cnblogs.com/mrfri/p/8550328.html
http://ex2tron.wang/opencv-python-contours/
啥叫轮廓
轮廓是一系列相连的点组成的曲线,代表了物体的基本外形。
轮廓与边缘很像。简单的说,轮廓是连续的,边缘并不全都连续。
寻找轮廓
"""
参数2:轮廓的查找方式,一般使用cv2.RETR_TREE,表示提取所有的轮廓并建立轮廓间的层级。
参数3:轮廓的近似方法。比如对于一条直线,我们可以存储该直线的所有像素点,也可以只存储起点和终点。使用cv2.CHAIN_APPROX_SIMPLE就表示用尽可能少的像素点表示轮廓。
"""
image, contours, hierarchy = cv2.findContours(
thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
边缘检测
物体的边缘检测是物体识别常用的手段。边缘检测常用亮度梯度方法。通过识别亮度梯度变化最大的像素点从而检测出物体的边缘。
常用边缘检测算法相关API:
# 索贝尔边缘识别
# cv.CV_64F:卷积运算使用数据类型为64位浮点型(保证微分的精度)
# 1:是否在水平方向索贝尔偏微分
# 0:是否在垂直方向索贝尔偏微分
# ksize:卷积核为5*5的方阵
cv.Sobel(original, cv.CV_64F, 1, 0, ksize=5)
# 拉普拉斯边缘识别
cv.Laplacian(original, cv.CV_64F)
# Canny边缘识别
# 50:水平方向阈值 240:垂直方向阈值
cv.Canny(original, 50, 240)
案例:
import cv2 as cv
original = cv.imread( '../data/chair.jpg', cv.IMREAD_GRAYSCALE)
cv.imshow('Original', original)
hsobel = cv.Sobel(original, cv.CV_64F, 1, 0, ksize=5)
cv.imshow('H-Sobel', hsobel)
vsobel = cv.Sobel(original, cv.CV_64F, 0, 1, ksize=5)
cv.imshow('V-Sobel', vsobel)
sobel = cv.Sobel(original, cv.CV_64F, 1, 1, ksize=5)
cv.imshow('Sobel', sobel)
laplacian = cv.Laplacian(original, cv.CV_64F)
cv.imshow('Laplacian', laplacian)
canny = cv.Canny(original, 50, 240)
cv.imshow('Canny', canny)
cv.waitKey()
亮度提升
OpenCV提供了直方图均衡化的方式实现亮度提升,更有利于边缘识别与物体识别模型的训练。
OpenCV直方图均衡化相关API:
# 彩色图转为灰度图
gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
# 直方图均衡化
equalized_gray = cv.equalizeHist(gray)
案例:
import cv2 as cv
original = cv.imread('../../data/sunrise.jpg')
cv.imshow('Original', original)
gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
cv.imshow('Gray', gray)
equalized_gray = cv.equalizeHist(gray)
cv.imshow('Equalized Gray', equalized_gray)
# YUV:亮度,色度,饱和度
yuv = cv.cvtColor(original, cv.COLOR_BGR2YUV)
yuv[..., 0] = cv.equalizeHist(yuv[..., 0])
equalized_color = cv.cvtColor(yuv, cv.COLOR_YUV2BGR)
cv.imshow('Equalized Color', equalized_color)
cv.waitKey()
角点检测
平直棱线的交汇点(颜色梯度方向改变的像素点的位置)
OpenCV提供的角点检测相关API:
gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
# Harris角点检测器
# 边缘水平方向、垂直方向颜色值改变超过阈值7、5时即为边缘
# 边缘线方向改变超过阈值0.04弧度即为一个角点。
corners = cv.cornerHarris(gray, 7, 5, 0.04)
案例:
import cv2 as cv
original = cv.imread('../data/box.png')
cv.imshow('Original', original)
gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
cv.imshow('Gray', gray)
corners = cv.cornerHarris(gray, 7, 5, 0.04)
mixture = original.copy()
mixture[corners > corners.max() * 0.01] = [0, 0, 255]
cv.imshow('Corner', mixture)
cv.waitKey()
图像识别
特征点检测
常用特征点检测有:STAR特征点检测 / SIFT特征点检测
特征点检测结合了边缘检测与角点检测从而识别出图形的特征点。
STAR特征点检测相关API如下:
import cv2 as cv
# 创建STAR特征点检测器
star = cv.xfeatures2d.StarDetector_create()
# 检测出gray图像所有的特征点
keypoints = star.detect(gray)
# drawKeypoints方法可以把所有的特征点绘制在mixture图像中
cv.drawKeypoints(original, keypoints, mixture,
flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv.imshow('Mixture', mixture)
案例:
import cv2 as cv
original = cv.imread('../data/table.jpg')
cv.imshow('Original', original)
gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
cv.imshow('Gray', gray)
star = cv.xfeatures2d.StarDetector_create()
keypoints = star.detect(gray)
mixture = original.copy()
cv.drawKeypoints(
original, keypoints, mixture,
flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv.imshow('Mixture', mixture)
cv.waitKey()
SIFT特征点检测相关API:
import cv2 as cv
# 创建SIFT特征点检测器
sift = cv.xfeatures2d.SIFT_create()
keypoints = sift.detect(gray)
案例:
import cv2 as cv
original = cv.imread('../data/table.jpg')
cv.imshow('Original', original)
gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
cv.imshow('Gray', gray)
sift = cv.xfeatures2d.SIFT_create()
keypoints = sift.detect(gray)
mixture = original.copy()
cv.drawKeypoints(original, keypoints, mixture,
flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv.imshow('Mixture', mixture)
cv.waitKey()
特征值矩阵
图像特征值矩阵(描述)记录了图像的特征点以及每个特征点的梯度信息,相似图像的特征值矩阵也相似。这样只要有足够多的样本,就可以基于隐马尔科夫模型进行图像内容的识别。
特征值矩阵相关API:
sift = cv.xfeatures2d.SIFT_create()
keypoints = sift.detect(gray)
_, desc = sift.compute(gray, keypoints)
案例:
import cv2 as cv
import matplotlib.pyplot as mp
original = cv.imread('../data/table.jpg')
cv.imshow('Original', original)
gray = cv.cvtColor(original, cv.COLOR_BGR2GRAY)
cv.imshow('Gray', gray)
sift = cv.xfeatures2d.SIFT_create()
keypoints = sift.detect(gray)
_, desc = sift.compute(gray, keypoints)
print(desc.shape)
mp.matshow(desc, cmap='jet', fignum='Description')
mp.title('Description', fontsize=20)
mp.xlabel('Feature', fontsize=14)
mp.ylabel('Sample', fontsize=14)
mp.tick_params(which='both', top=False, labeltop=False, labelbottom=True, labelsize=10)
mp.show()
物体识别
import os
import numpy as np
import cv2 as cv
import hmmlearn.hmm as hl
def search_files(directory):
directory = os.path.normpath(directory)
objects = {}
for curdir, subdirs, files in os.walk(directory):
for file in files:
if(file.endswith('.jpg')):
label = curdir.split(os.path.sep)[-1]
if label not in objects:
objects[label] = []
path = os.path.join(curdir, file)
objects[label].append(path)
return objects
#加载训练集样本数据,训练模型,模型存储
train_objects = search_files('../data/objects/training')
train_x, train_y = [], []
for label, filenames in train_objects.items():
descs = np.array([])
for filename in filenames:
image = cv.imread(filename)
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
#范围缩放,使特征描述矩阵样本数量一致
h, w = gray.shape[:2]
f = 200 / min(h, w)
gray = cv.resize(gray, None, fx=f, fy=f)
sift = cv.xfeatures2d.SIFT_create()
keypoints = sift.detect(gray)
_, desc = sift.compute(gray, keypoints)
if len(descs) == 0:
descs = desc
else:
descs = np.append(descs, desc, axis=0)
train_x.append(descs)
train_y.append(label)
models = {}
for descs, label in zip(train_x, train_y):
model = hl.GaussianHMM(n_components=4, covariance_type='diag', n_iter=100)
models[label] = model.fit(descs)
#测试模型
test_objects = search_files('../data/objects/testing')
test_x, test_y = [], []
for label, filenames in test_objects.items():
descs = np.array([])
for filename in filenames:
image = cv.imread(filename)
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
sift = cv.xfeatures2d.SIFT_create()
keypoints = sift.detect(gray)
_, desc = sift.compute(gray, keypoints)
if len(descs) == 0:
descs = desc
else:
descs = np.append(descs, desc, axis=0)
test_x.append(descs)
test_y.append(label)
# 遍历所有测试样本 使用model匹配测试样本查看每个模型的匹配分数
for descs, test_label in zip(test_x, test_y):
for pred_label, model in models.items():
score = model.score(descs)
print(test_label, '->', pred_label, score)