- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
OpenCV是一个(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。 在学习过程中遇到问题最好的办法就是查询 opencv官方文档 .
图像的读取、显示与写入分别对应三个函数, cv2. imread() 、 cv2.imshow() 、 cv2.imwrite() .
语法:cv2.imread(filename[, flags])--->image 参数: filename---文件路径(相对路径和绝对路径),路径中不要带有中文。 flags---可选标志,用于指定读取图像的样式,常见的有 cv2.IMREAD_UNCHANGED (-1)、 cv2.IMREAD_GRAYSCALE (0)、 cv2.IMREAD_COLOR (1)。默认为1。 注意:opencv读取彩色图像的格式是BGR,而大多数视觉库使用的是RGB,因此当将 OpenCV 与其他工具包一起使用时,当从一个库切换到另一个库时,不要忘记交换蓝色和红色通道.
语法:cv2.imshow(window_name, image)--->None 参数: window_name---显示图像的窗口的名字。 image---显示图像的变量名。 注意:该函数一般和 cv2.waitKey() 、 cv2.destroyAllWindows() 、 cv2.destroyWindow() 一起使用。 cv2.waitKey() 函数是键盘绑定函数,等待键击任意键或指定键继续程序。 cv2.destroyAllWindows() 用于销毁全部窗口(从内存中清除)、 cv2.destroyWindow() 销毁指定窗口(从内存中清除).
import cv2
# 查看opencv版本
print(cv2.getVersionString())
# 读取图像
image_unchanged = cv2.imread("image\\cat.jpg", cv2.IMREAD_UNCHANGED)
image_grayscale = cv2.imread("image\\cat.jpg", cv2.IMREAD_GRAYSCALE)
image_color = cv2.imread("image\\cat.jpg", cv2.IMREAD_COLOR)
# 显示图像
cv2.imshow('unchanged', image_unchanged)
cv2.imshow('grayscale', image_grayscale)
cv2.imshow('color', image_color)
# 永远暂停程序直到键击任意键
cv2.waitKey(0)
# 销毁所有窗口
cv2.destroyAllWindows()
运行结果:
语法:cv2.imwrite(filename, image[, params]) 参数: filename---文件名,它必须包含文件的扩展名(如, .jpg 、 .png ) image----要保存的图像变量名.
import cv2
image_grayscale = cv2.imread("image\\cat.jpg", cv2.IMREAD_GRAYSCALE)
cv2.imshow('grayscale', image_grayscale)
cv2.imwrite("image\\cat_grayscale.jpg", image_grayscale)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
常用的图像色彩空间有RGB、Lab、YCrCb、HSV等模式, RGB模式 :通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各种颜色。依赖于光线。R、G、B的取值范围:[0, 255]。容易理解,但连续变换时不直观。 详见: RGB_百度百科 Lab模式 :它是一种设备无关的颜色模型,也是一种基于生理特征的颜色模型。它由亮度(L)、颜色(a:绿色到洋红色;b:蓝色到黄色)组成。 详见: Lab颜色模型_百度百科 YCrCb模式 :它派生自RGB颜色模式,主要应用在优化彩色视频信号的传输,使其向后相容老式黑白电视。Y:伽马校正后从RGB获得的亮度或亮度分量;Cr:反映了RGB输入信号红色部分与RGB信号亮度值之间的差异;Cb:反映的是RGB输入信号蓝色部分与RGB信号亮度值之间的差异。 HSV模式 :它是H、S、V三维颜色空间中的一个可见光子集。是为了数字化图像提出来的,不能很好的表示人眼解释图像的过程。H:色相;S:饱和度;V:明度。取值范围,H---[0, 179],S---[0, 255],V---[0, 255]。 详见: HSV颜色模型_百度百科 灰度图 :图像是有不同灰度的像素组成的,每个像素都是在[0, 255]取值。根据人眼敏感度,把RGB图片转换为灰度图,不是简单把RGB每个通道取平均值而是:Y = 0.299 R + 0.587 G + 0.114*B 注意:必须要指出的是图像就是一个数组,因此可以使用 image.shape 查询形状,其输出结果为 (pixel_height, pixel_width, 3) 。 pixel_height 、 pixel_width 代表图像的像素尺寸, 3 代表每个像素的颜色是由一个三维数组确定的(opencv中是BGR模式的数组)。 如何查看某像素的RGB、Lab、YCrCb、HSV值? 由于在读取图像时直接读取的是RGB值,因此可通过 cv2.imshow() 函数直接查看。 利用 cv2.cvtColor(np.uint8([[[image[x, y]]]]), cv2.COLOR_BGR2Lab)[0][0] 查看位置(x, y)处的Lab值。YCrCb、HSV与Lab类似。 注意:图像中某位置的图像色彩空间转换为其他时,必须要使其BGR "空间值"(由三个通道组成)三维化。 练习:如何通过鼠标的移动实时获取指定图片的不同色彩空间值?
import cv2
import glob
import numpy as np
# 定义鼠标回调的‘动作’函数
def showPixelValue(event, x, y, flags, param):
# 定义全局变量
global img, combinedResult, placeholder
# 判断鼠标是否移动
if event == cv2.EVENT_MOUSEMOVE:
# 获取鼠标坐标的RGB值
bgr = img[y, x]
# 转换色彩空间值
ycb = cv2.cvtColor(np.uint8([[bgr]]), cv2.COLOR_BGR2YCrCb)[0][0]
lab = cv2.cvtColor(np.uint8([[bgr]]), cv2.COLOR_BGR2Lab)[0][0]
hsv = cv2.cvtColor(np.uint8([[bgr]]), cv2.COLOR_BGR2HSV)[0][0]
# 创建一个与载入图片高度相同的黑色图块,作为结果区域
placeholder = np.zeros((img.shape[0], 400, 3), dtype=np.uint8)
# 在结果区域内显示鼠标位置不同色彩空间的值
cv2.putText(placeholder, "BGR {}".format(bgr), (20, 70), cv2.FONT_HERSHEY_COMPLEX, .9, (255, 255, 255), 1,
cv2.LINE_AA)
cv2.putText(placeholder, "HSV {}".format(hsv), (20, 140), cv2.FONT_HERSHEY_COMPLEX, .9, (255, 255, 255), 1,
cv2.LINE_AA)
cv2.putText(placeholder, "YCrCb {}".format(ycb), (20, 210), cv2.FONT_HERSHEY_COMPLEX, .9, (255, 255, 255), 1,
cv2.LINE_AA)
cv2.putText(placeholder, "LAB {}".format(lab), (20, 280), cv2.FONT_HERSHEY_COMPLEX, .9, (255, 255, 255), 1,
cv2.LINE_AA)
# 合并加载的图片和结果区域
combinedResult = np.hstack([img, placeholder])
# 显示合并后的图像
cv2.imshow('PRESS P for Previous, N for Next Image', combinedResult)
if __name__ == '__main__':
# 读取指定的图片,并转换尺寸
files = glob.glob('image/rub*.jpg')
files.sort()
img = cv2.imread(files[0])
img = cv2.resize(img, (400, 400))
# 显示转换尺寸后的图像(在‘PRESS...’显示框加载)
cv2.imshow('PRESS P for Previous, N for Next Image', img)
# 创建一个无加载图像的显示框,并覆盖了原‘PRESS...’显示框
cv2.namedWindow('PRESS P for Previous, N for Next Image')
# 创建一个鼠标回调,加载回调‘动作’函数,并且该动作在‘PRESS...’显示框内实现
cv2.setMouseCallback('PRESS P for Previous, N for Next Image', showPixelValue)
i = 0
while 1:
k = cv2.waitKey(1) & 0xFF
# 键击‘n’切换到下一张图片
if k == ord('n'):
i += 1
img = cv2.imread(files[i % len(files)])
img = cv2.resize(img, (400, 400))
cv2.imshow('PRESS P for Previous, N for Next Image', img)
# 键击‘p’切换到上一张图片
elif k == ord('p'):
i -= 1
img = cv2.imread(files[i % len(files)])
img = cv2.resize(img, (400, 400))
cv2.imshow('PRESS P for Previous, N for Next Image', img)
# 键击‘Esc’退出程序
elif k == 27:
cv2.destroyAllWindows()
break
运行结果如下(静态展示):
图像编辑主要包括调整图像大小、裁剪图像、图像的旋转和移动、翻转,透视变换.
在opencv中使用 image.shape 获得图像大小, image.shape 获得结果是 (heigh, width, channels) 即高度、宽度、通道数。在opencv中使用resize()函数调整图像的大小。 语法:cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])--->dst 参数: src---源图像。 dsize---可为tuple参数或None。目标图像的大小,即新的图像宽高。需要注意的是,dsize=(width, height)中第 一个参数是图像宽度第二个是高度,与shape=(height, width, channels)的相对位置刚好相反。 dst---目标图像,在python中无任何意义,一般不传参或设成None。 fx---沿水平轴的比例因子。fy---沿垂直轴的比例因子。 interpolation---插值方式,提供了调整图像大小的不同方法。其本质是一个int数值,不过一般用opencv内置的参 数名称以提高可读性。默认为 cv2.INTER_LINEAR 。其他还有 cv2.INTER_AREA 、 cv2.INTER_CUBIC 、 cv2.INTER_NEAREST 。 注意:当目标图像与源图像的宽高比不一致,代表经缩放后图像失真。 关于插值参数,①要缩小图片选用 cv2.INTER_AREA ;②放大图片选用 cv2.INTER_CUBIC (速度慢)或 cv2.INTER_LINEAR (速度快,效果还行).
import cv2
cat = cv2.imread('image\\cat.jpg')
# 指定目标图像的宽高
# 图像不失真
cat_down_0 = cv2.resize(cat, (150, 150))
cat_up_0 = cv2.resize(cat, (500, 500))
cv2.imshow('cat_down-No distortion', cat_down_0)
cv2.imshow('cat_up-No distortion', cat_up_0)
# 图像失真
cat_distortion = cv2.resize(cat, (200, 300))
cv2.imshow('cat distortion', cat_distortion)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 指定图像的比例因子(需要指出的是dsize必须要传参,None)
# 图像不失真
cat_down_1 = cv2.resize(cat, None, fx=0.75, fy=0.75)
cat_up_1 = cv2.resize(cat, None, fx=1.2, fy=1.2)
cv2.imshow('cat_down-No distortion1', cat_down_1)
cv2.imshow('cat_up-No distortion1', cat_up_1)
# 图像失真
cat_distortion1 = cv2.resize(cat, None, fx=0.75, fy=1.2)
cv2.imshow('cat distortion1', cat_distortion1)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 指定图像缩放方法
cat_scale0 = cv2.resize(cat, (150, 150), interpolation=cv2.INTER_AREA)
cat_scale1 = cv2.resize(cat, (450, 450), interpolation=cv2.INTER_CUBIC)
cat_scale2 = cv2.resize(cat, (450, 450), interpolation=cv2.INTER_LINEAR)
cat_scale3 = cv2.resize(cat, (150, 150), interpolation=cv2.INTER_NEAREST)
cv2.imshow('cat_scale0', cat_scale0)
cv2.imshow('cat_scale1', cat_scale1)
cv2.imshow('cat_scale2', cat_scale2)
cv2.imshow('cat_scale3', cat_scale3)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果(部分)如下:
裁剪图像是为了从图像中删除不需要的对象或区域;也就是删除其他,保留想要的图像区域。 opencv中的图像裁剪是利用numpy数组切片的方法来实现的。首先,我们要知道图像就是一个数组,其由高、宽、通道数三个维度组成。仅对图像进行裁剪,就是意味着对宽和高两个维度进行切片操作。具体如下: 语法:image[start_row : end_row, start_col : end_col]--->dst 参数:start_row 、end_row---图像的开始与结束的行坐标。 start_col :、end_col---图像的开始与结束的列坐标。 注意:图像可视为坐标系,坐标原点为图像的左上角顶点,裁剪区域即是四条直线的围合区域.
import cv2
cat = cv2.imread('image\\cat.jpg')
print(cat.shape)
# 裁剪图像
cropped_cat = cat[25:190, 50:300]
cv2.imshow('original', cat)
cv2.imshow('cropped', cropped_cat)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
图像的旋转和平移是图像编辑中最基本的操作之一。旋转和平移都属于同一种图像编辑类型,即放射变换(Affine transformations)类型。 旋转和平移都是借助 cv2.warpAffine() 函数实现的,其区别是转换矩阵M的不同。旋转利用 cv2.getRotationMatrix2D() 函数构造旋转矩阵,平移矩阵利用二维数组构建.
首先看一下仿射变换函数, cv.warpAffine() 。需要指出的是,图像变换时其坐标系是不变的,与源图像一致。 语法:cv2.warpAffine(src, M, desize[, dst[, flags[, borderMode[, borderValue]]]])--->dst 参数: src---源图像。M---转换矩阵。 desize---tuple类型参数。目标图像的宽高,即新的图像宽高(新的图像可能包含部分或者全部变换后的图像,其他区域一般由黑色像素填充)。坐标系遵循源图像坐标系。 dst---目标图像,在python中无任何意义,一般不传参或设成None。 flags---插值方法。 cv2.INTER_NEAREST ,最近邻插值; cv2.INTER_LINEAR ,线性插值(默认值); cv2.INTER_AREA ,区域插值; cv2.INTER_CUBIC ,三次样条插值; cv2.INTER_LANCZOS4 ,Lanczos插值,等。 borderMode---边界像素模式。 borderValue---边界填充值(即显示框内图像之外的区域),默认值为0.
旋转矩阵的构造, cv2.getRotationMatrix2D() 。 语法:cv2.getRotationMatrix2D(center, angle, scale)--->M 参数: center---图像的旋转中心,是一个元组类型的参数。即在图像构建的坐标系中选取旋转中心。 angle---旋转角度(角度制),正负值遵循坐标系角度规则。 scale---图像的缩放比例因子.
平移矩阵的构造。 构造数组,2Darray = np.ndarray([[1 0 tx], [0, 1, ty]]) 参数:tx---正值,图像将右移|tx|个像素;负值,将向左移|tx|个像素。 ty---正值,图像将下移|ty|个像素;负值,将向上移|ty|个像素 平移矩阵,M = 2Darray 。
import cv2
import numpy as np
cat = cv2.imread('image\\cat.jpg')
print(cat.shape)
height, width = cat.shape[:2]
# 旋转图像
center = (width/3, height/10)
rotate_matrix = cv2.getRotationMatrix2D(center, -15, scale=1.2)
rotated_cat = cv2.warpAffine(cat, rotate_matrix, dsize=(500, 300))
# 原图像以坐标(width/3, height/10)为旋转中心旋转-15度然后再缩放1.2倍
cv2.imshow('original image', cat)
cv2.imshow('rotated_cat', rotated_cat)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 平移图像
tx, ty = width/5, -height/5
translation_matrix = np.array([[1, 0, tx], [0, 1, ty]])
translation_cat = cv2.warpAffine(cat, translation_matrix, dsize=(500, 300))
cv2.imshow('original image', cat)
cv2.imshow('translation_cat', translation_cat)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
图像翻转,也就是通常所说的镜像。主要包括垂直翻转、水平翻转、水平垂直翻转。 语法:cv2.flip(src, flipCode[, dst])--->dst 参数: src---源图像。 flipCode---翻转方式。 flipCode == 0 ,垂直翻转(沿x轴翻转); flipCode>0 ,水平翻转(沿y轴翻转); flipCode< 0 ,水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°).
import cv2
import numpy as np
cat = cv2.imread('image\\cat.jpg')
# 翻转图像
flip_cat1 = cv2.flip(cat, flipCode=0)
flip_cat2 = cv2.flip(cat, flipCode=1)
flip_cat3 = cv2.flip(cat, flipCode=-1)
combined = np.hstack((cat, flip_cat1, flip_cat2, flip_cat3))
cv2.imshow('combined', combined)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果:
注释图像即是在图像添加注释性信息,通常是绘制图形标出对象或者添加文字信息。需要指出的是在进行注释图像前一定要使用 copy() 方法创建副本,以确保源图像的完整性.
语法:cv2.line(image, start_point, end_point, color, thickness)--->image 参数: image---图像,通常是源图像的副本。 start_point---直线的起点坐标,元组参数。 end_point---直线的终点坐标,元组参数。 color---直线的颜色,元组参数。 thickness---直线的厚度。 注意:start_point与end_point的值第一个值代表的是横轴值,第二个值代表的是竖轴值,可简单理解为x、y轴的值.
import cv2
girl = cv2.imread('image\\girl.jpg')
print(girl.shape)
# 绘制一条长260像素的横向白色线
girl_line = girl.copy()
cv2.line(girl_line, (0, 250), (260, 250), (255, 255, 255), thickness=2)
cv2.imshow('girl_line', girl_line)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
语法:cv2.circle(image, center_coordinates, radius, color[, thickness[, lineType[, shift]]])--->image 参数: image---图像,通常是源图像的副本。 center_coordinates---圆形的圆心坐标,元组参数。 radius---圆形的半径。 color---直线的颜色,元组参数。 thickness---正值,直线的厚度;负值表示绘制一个填充圆。 lineType---圆边线类型。 shift---圆心和半径值中的小数位数。 注意:start_point与end_point的值第一个值代表的是横轴值,第二个值代表的是竖轴值,可简单理解为x、y轴的值.
import cv2
# 绘制一个圆形
girl = cv2.imread('image\\girl.jpg')
girl_circle = girl.copy()
cv2.circle(girl_circle, (380, 270), 175, (255, 255, 255), thickness=2)
cv2.imshow('girl_circle', cv2.resize(girl_circle, None, fx=0.6, fy=0.6))
# 绘制一个填充圆
girl_circle1 = girl.copy()
cv2.circle(girl_circle1, (380, 270), 175, (255, 255, 255), thickness=-1)
cv2.imshow('girl_circle1', cv2.resize(girl_circle1, None, fx=0.6, fy=0.6))
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
语法:cv2.rectangle(image, start_point, end_point, color[, thickness[, lineType[, shift]]])--->image 参数: image---图像,通常是源图像的副本。 start_point---矩形的起始点,即其左上角,元组参数。 end_point---矩形的终点,即其右下角, 元组参数。 color---直线的颜色,元组参数。 thickness---正值,直线的厚度;负值表示绘制一个填充矩形。 lineType---矩形边线类型。 shift---坐标中的小数位数。 注意:start_point与end_point的值第一个值代表的是横轴值,第二个值代表的是竖轴值,可简单理解为x、y轴的值.
import cv2
# 绘制一个矩形
girl = cv2.imread('image\\girl.jpg')
print(girl.shape)
girl_rec = girl.copy()
cv2.rectangle(girl_rec, (200, 170), (570, 400), (255, 255, 255), thickness=2)
cv2.imshow('girl_rec', cv2.resize(girl_rec, None, fx=0.6, fy=0.6))
# 绘制一个填充矩形
girl_rec1 = girl.copy()
cv2.rectangle(girl_rec1, (200, 170), (570, 400), (255, 255, 255), thickness=-2)
cv2.imshow('girl_rec1', cv2.resize(girl_rec1, None, fx=0.6, fy=0.6))
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
语法:cv2.ellipse(image, center, axesLength, angle, startAngle, endAngle, color[, thickness[,lineType[, shift]]])--->image 参数: image---图像,通常是源图像的副本。 centert---椭圆的圆心坐标,元组参数。 axesLength---椭圆长轴与短轴大小的一半,元组参数。 angle---椭圆的旋转角度(角度制)。 startAngle---椭圆弧的起始角度。 endAngle---椭圆弧的结束角度。 color---直线的颜色,元组参数。 thickness---正值,直线的厚度;负值表示绘制一个填充矩形。 lineType---椭圆边线类型。 shift---坐标中的小数位数。 注意:①椭圆的角度均遵循顺逆时针规则,x轴正半轴为起始轴,顺时针为正,逆时针为负。②先绘制正常的椭圆,然后再根据参数angle以center为旋转中心旋转椭圆.
import cv2
# 绘制一个椭圆
girl = cv2.imread('image\\girl.jpg')
print(girl.shape)
girl_ellipse = girl.copy()
cv2.ellipse(girl_ellipse, (380, 300), (200, 100), 45, 180, 360, (255, 255, 255), thickness=2)
cv2.ellipse(girl_ellipse, (380, 300), (200, 100), 45, 0, 180, (0, 0, 255), thickness=2)
cv2.imshow('girl_ellipse', girl_ellipse)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
语法:cv2.putText(image, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])--->image 参数: image---图像,通常是源图像的副本。 text---文本字符串。 org---text的左下角的位置。 fontFace---字体类型。 fontScale---字体的比例(用于乘以特定字体的基大小)。 color---字体颜色。 thickness---文本字体线条的粗细 lineType---字体边线类型。 bottomLeftOrigin---如果为 true,则图像数据原点位于左下角。否则,它位于左上角.
import cv2
girl = cv2.imread('image\\girl.jpg')
print(girl.shape)
girl_putText = girl.copy()
text = 'Finally, let’s try annotating images with text. To do this, use the function in OpenCV'
cv2.putText(girl_putText, text, (50, 400), cv2.QT_FONT_BLACK, 0.85, (255, 255, 255))
cv2.imshow('1111', girl_putText)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
鼠标回调函数和跟踪栏可以达到如下动图的效果: 鼠标指针是图形用户界面(GUI)中的关键组件,没有鼠标指针就无法真正做到图形界面的人机交互。opencv中与鼠标指针相关的有两个重要的函数,即鼠标回调函数和创建跟踪栏.
我们通过实现鼠标标注图像这一功能来了解鼠标回调函数。 首先,定义一个特殊的“回调”函数,该函数将在显示窗口中显示的图像上绘制一个矩形。仅在计算机检测到某些用户界面事件时调用的函数,被称为“回调”函数。由于这个函数与鼠标相关联,因此一般命名为MouseCallback,不过你也可以自定义。这个函数在调用时无需输入任何参数,这是因为在用户与鼠标进行交互时,所需参数会自动填充。 然后,将“回调”函数与特定的鼠标事件相关联。每当调用此函数(用户和鼠标交互)时,位于显示图像上的鼠标会记录鼠标x轴、y轴的坐标,并记录鼠标的事件类型和事件标志。对于本例,当触发动作 cv2.LEFTBUTTONDOWN 和 cv2.LEFTBUTTONUP 时,我们将坐标存储在各自的变量中,并使用它们来绘制矩形。 ①怎么定义鼠标回调函数? 语法:def MouseCallback(event, x, y, flags, *userdata) 参数: event---鼠标事件。 x, y---鼠标的x轴和y轴的坐标。 flags---事件标志。 *userdata---其他的输入数据。 注意:定义回调函数时,函数名是可变的。 ②怎么调用鼠标回调函数? 使用 cv2.setMouseCallback() 函数来调用鼠标回调函数。 语法:cv2.setMouseCallback(winname, onMouse[, userdata]) 参数: winname---窗口名称(必须是已经存在的窗口)。 onMouse---鼠标回调函数名。 userdata:传递给回调函数的可选参数。 ③怎么使显示窗口和用户发生交互? 利用while循环和cv2.waitKey()函数指定键盘字符发生交互。具体而言:
cv2.destroyAllWindows()
销毁窗口。 ④代码的具体实现.
# 作者:水果好好吃哦
# 日期:2023/6/29
import cv2
# 初始化参数
top_left_corner, bottom_right_corner = [], []
# flags,*userdata虽然没有调用,但不可缺少
def drawRectangle(event, x, y, flags, *userdata):
global top_left_corner, bottom_right_corner
if event == cv2.EVENT_LBUTTONDOWN:
top_left_corner = [(x, y)]
elif event == cv2.EVENT_LBUTTONUP:
bottom_right_corner = [(x, y)]
cv2.rectangle(image, top_left_corner[0], bottom_right_corner[0], (0, 255, 0), 2, 8)
cv2.imshow('Window', image)
image = cv2.imread('image\\cat.jpg')
temp = image.copy()
cv2.namedWindow('Window')
cv2.setMouseCallback('Window', drawRectangle)
k = 0
# 键击‘q’退出程序,键击‘c’重新绘制矩形
while k != 113:
cv2.imshow("Window", image)
k = cv2.waitKey(0)
if k == 99:
image = temp.copy()
cv2.imshow("Window", image)
cv2.destroyAllWindows()
运行过程截图:
跟踪栏回调函数与鼠标回调函数类似。 ①怎么定义跟踪栏回调函数? 语法:def TrackbarCallback( args) 参数: args---跟踪栏参数。args[0]可以检索跟踪栏条目位置,这是一个与用户发生交互的参数,其范围是(0, count]内的整数。 注意:定义回调函数时,函数名是可变的。 ②怎么调用跟踪栏回调函数? 使用 cv2.createTrackbar() 函数来调用跟踪栏回调函数。 语法:cv2.createTrackbar(trackbarName, winname, value, count, onChange[, userdata]) 参数: trackbarName---创建的跟踪栏名称。 winname---显示窗口的名称。 value---滑块的默认位置。 count---滑块可滑到的最大位置。 onChange---回调函数名。 userdata---传递给回调函数的用户数据。它可用于处理跟踪栏事件,而无需使用全局变量。 ③代码的具体实现.
import cv2
# 初始化参数
maxScaleUp = 100
scaleFactor = 1
windowName = "Resize Image"
trackbarValue = "Scale"
# 创建回调函数,图像可以在[1, 2]之间缩放
def scaleImage(*args):
scaleFactor = 1 + args[0]
scaledImage = cv2.resize(image, None, fx=scaleFactor, fy=scaleFactor, interpolation=cv2.INTER_LINEAR)
cv2.imshow(windowName, scaledImage)
image = cv2.imread("image\\jellyfish.jpg")
cv2.namedWindow(windowName, cv2.WINDOW_AUTOSIZE)
# 调用跟踪栏回调函数
cv2.createTrackbar(trackbarValue, windowName, scaleFactor, maxScaleUp, scaleImage)
cv2.imshow(windowName, image)
c = cv2.waitKey(0)
cv2.destroyAllWindows()
运行过程截图:
opencv使用卷积内核模糊(平滑)、锐化图像,也就是过滤图像。本节涉及到了卷积,建议对卷积有个基本了解,可参考: 如何通俗易懂地解释卷积? - 知乎 。
卷积核也被称为卷积矩阵,是用于过滤图像的二维矩阵,它通常是一个N阶方阵,其中N是奇数。卷积核的作用:使用卷积核对图像的每个像素执行数学运算,可以达到模糊(平滑)或锐化图像的目的.
前面已经提到,图像就是一个三维数组,当去掉通道维度时它就是一个矩阵,可用于参与矩阵的运算。比如,下图的噪点较多,此时我们可以使用卷积操作将图像进行平滑处理。具体过程如下: ①用矩阵表示图像; ②设置卷积核(卷积矩阵),做归一化处理(不改变图像的亮度)。(需要注意的是,图像处理中一般用的是正态分布矩阵,这里为了简单,就用了算术平均矩阵) ③卷积计算。若要平滑a1,1点,就在矩阵中,取出a1,1 点附近的点组成矩阵 f(形状与卷积矩阵相同) ,和卷积核进行卷积计算后,再填回去: 下面用一个动图诠释计算过程, ④写成一个公式:
这里的仅简单介绍 cv2.filter() 函数。 语法:cv2.filter2D(src, ddepth, kernel)--->dst 参数: src---源图像。 ddepth---目标图像的深度值。-1,表示目标图像与源图像的深度相同。 kernel---内核,也就是卷积核。矩阵。 注意:array = np.array[[0, 0, 0], [0, 1, 0], [0, 0, 0]],是一个恒等核,根据上述卷积计算可知经过恒等核过滤后的目标图像与源图像是一样的(外观上是一样的).
import cv2
import numpy as np
car = cv2.imread('image\\car.jpg')
# 创建恒等核
kernel1 = np.array([[0, 0, 0],
[0, 1, 0],
[0, 0, 0]])
# 使用filter2D()函数执行线性滤波操作
smooth_car = cv2.filter2D(car, ddepth=-1, kernel=kernel1)
combine = np.hstack((car, smooth_car))
# 显示原始图像和经过恒等核过滤后的图像
cv2.imshow('original image on the left, smooth image on the right', combine)
cv2.waitKey(0)
cv2.imwrite('image\\smooth_car.jpg', smooth_car)
cv2.destroyAllWindows()
运行结果如下:
# 作者:水果好好吃哦
# 日期:2023/6/30
import cv2
import numpy as np
car = cv2.imread('image\\car.jpg')
# 创建算术平方根核
kernel1 = np.ones((3, 3), 'f4')/9
# 使用filter2D()函数执行线性滤波操作
smooth_car = cv2.filter2D(car, ddepth=-1, kernel=kernel1)
combine = np.hstack((car, smooth_car))
# 显示原始图像和经过恒等核过滤后的图像
cv2.imshow('original image on the left, smooth image on the right', combine)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
语法:cv2.blur(src, ksize[, dst[, anchor[, borderType]]])--->dst 参数: src---源图像。 ksize---模糊函数的内核尺寸,元组类型参数。 dst---目标图像,在python中无任何意义,一般不传参或设成None。 borderType---制作图形边界。 注意:该函数与使用算术平方根核模糊的图像结果完全相同.
# 作者:水果好好吃哦
# 日期:2023/6/30
import cv2
import numpy as np
car = cv2.imread('image\\car.jpg')
smooth_car = cv2.blur(car, ksize=(3, 3))
combine = np.hstack((car, smooth_car))
cv2.imshow('original image on the left, smooth image on the right', combine)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
高斯模糊使用了高斯滤波器(即使用高斯(又称正太)分布作为滤波函数),该滤波器执行加权平均值,根据像素值与内核中心的距离对像素值进行加权,离中心较远的像素对加权平均值的影响较小。 语法:cv2.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])--->dst 参数: src---源图像。 ksize---高斯核的尺寸,元组类型参数,一般设置为奇数方阵。 sigmaX---X方向上的高斯核标准差。 dst---目标图像,在python中无任何意义,一般不传参或设成None。 sigmaY---Y方向上的高斯核标准差。 sigmaY=0 ,表示 sigmaY=sigmaX ; sigmaY=0 , sigmaX=0 ,表示X和Y方向的高斯核标准差需要根据 ksize.width 、 ksize.height 计算;为了完全控制结果,建议指定ksize、sigmaX和sigmaY的值。 borderType---制作图形边界.
import cv2
import numpy as np
car = cv2.imread('image\\car.jpg')
gaussian_blur_car = cv2.GaussianBlur(car, (3, 3), sigmaX=1, sigmaY=0)
combine = np.hstack((car, gaussian_blur_car))
cv2.imshow('original image on the left, smooth image on the right', combine)
cv2.waitKey(0)
cv2.imwrite('image\\gaussian_blur_car.jpg', gaussian_blur_car)
cv2.destroyAllWindows()
运行结果如下:
中值模糊是在源图像中的每个像素都替换为内核区域中图像像素的中值。 语法:cv2.medianBlur(src, ksize[, dst])--->dst 参数:src---源图像。 ksize---内核的大小,其必须是大于1的奇数。 注意:对于相同的内核大小,中值模糊的效果比高斯模糊更突出.
# 作者:水果好好吃哦
# 日期:2023/6/30
import cv2
import numpy as np
car = cv2.imread('image\\car.jpg')
median_blur_car = cv2.medianBlur(car, 3)
combine = np.hstack((car, median_blur_car))
cv2.imshow('original image on the left, smooth image on the right', combine)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
我们也可以利用卷积核锐化图像,其实现依赖于 cv2.filter2D() 函数.
import cv2
import numpy as np
car = cv2.imread('image\\car.jpg')
# 创建锐化内核(归一化处理,保持亮度不变)
kernel1 = np.array([[0, -1, 0],
[-1, 3, 0],
[0, 0, 0]])
# 使用filter2D()函数执行线性滤波操作
sharp_car = cv2.filter2D(car, ddepth=-1, kernel=kernel1)
combine = np.hstack((car, sharp_car))
# 显示原始图像和经过自定义内核锐化后的图像
cv2.imshow('original image on the left, smooth image on the right', combine)
cv2.waitKey(0)
cv2.imwrite('image\\sharp_car.jpg', sharp_car)
cv2.destroyAllWindows()
运行结果如下:
通常,单一使用图像模糊或锐化是达不到预期效果的,此时我们可以选择使用双边滤波方法让二者共同作用在图像中。双边滤波本质上是对图像应用2D高斯模糊,且考虑了相邻像素强度的变化(用以降低边缘区域的高斯权重)。 让我们先来探讨下双边滤波的工作过程。假如我们正在过滤图像中靠近边缘的区域,普通的高斯模糊会根据实际权重模糊边缘,但是双边滤波器可以通过感知边缘(通过像素强度的差异)主动降低边缘区域的权重,从而减少高斯模糊对边缘的影响。因此,像素强度更均匀的区域更加模糊,边缘区域几乎无模糊。 图像中的过滤像素的最终值是由空间和强度权重决定,因此 。
opencv中利用 cv2.bilateraFilter() 函数实现双边过滤。 语法:cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])--->dst 参数: src---源图像。 d---定义用于过滤的像素邻域的直径。 sigmaColor---颜色(像素)强度标准差,定义一维高斯分布,该分布指定允许的像素强度变化的程度。 sigmaSpace---x和y轴组成的空间的标准差。 borderType---制作图形边界.
import cv2
import numpy as np
car = cv2.imread('image\\car.jpg')
bilateral_filter_car = cv2.bilateralFilter(car, d=5, sigmaColor=40, sigmaSpace=50)
combine = np.hstack((car, bilateral_filter_car))
# 显示原始图像和经过自定义内核锐化后的图像
cv2.imshow('original image on the left, smooth image on the right', combine)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
阈值又叫临界值,是指一个效应能够产生的最低值或最高值。图像的阈值是指灰度阈值,是针对灰度图像的。 阈值处理是指剔除图像内像素值高于阈值或低于阈值的像素点。利用图像阈值可以实现图像分割,即阈值分割法。根据阈值分割规则的作用区域可分为全局阈值分割和局部阈值分割两种。全局阈值分割方法包括直方图计数法、熵算法、Otsu算法;局部阈值分割是指自适应阈值法。 语法:cv2.threshold(src, thresh, maxValue, type[, dst])--->retval, dst 参数: src---源图像。thresh---阈值。 maxValue---与 cv2.THRESH_BINARY 和 cv2.THRESH_BINARY_INV 阈值类型一起使用的最大值。 type---阈值类型。 cv2.THRESH_BINARY 、 cv2.THRESH_BINARY_INY 、 cv2.THRESH_TRUNC 、 cv2.THRESH_TOZERO 、 cv2.THRESH_TOZERO_INV 、 cv2.THRESH_MASK 、 cv2.THRESH_OTSU 、 cv2.THRESH_TRIANGLE 。 返回值:retval---阈值。dst---目标图像。 这里只简单的演示使用 cv2.THRESH_BINARY 阈值类型的示例,仅作为入门了解.
import cv2
import numpy as np
# 需要对图像做预处理——转化为灰度图
src = cv2.imread('image\\cat.jpg', cv2.IMREAD_GRAYSCALE)
retval, dst = cv2.threshold(src, 100, 255, cv2.THRESH_BINARY)
print(retval)
combine = np.hstack((src, dst))
cv2.imshow('original image on the left, smooth image on the right', combine)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
Blob,译文是“(颜色)的一小片,斑点”。它是图像中拥有共同属性(例如灰度值)的连通域,上图中的Blob就是灰色的连通域。Blob检测就是为了识别并标记这些连通域。 就上图而言,使用Blob检测可以达到如下效果: Blob检测是通过 cv2.SimpleBlobDeterctor 算子实现的.
filterByColor=1
(或True)。其次设置颜色倾向, blobColor=0
以选择较暗的blob, blobColor=255
以选择较浅的区域。 filterByArea=1
(或True)。其次设置minArea和maxArea的适当值。例如,设置minArea=50,maxArea=234,将滤除所有少于50和多于234个像素点的blob。 filterByCircularity=1
(或True),然后设置适当的 minCircularity
和 maxCircularity
。 filterByConvexity=1
(或True),然后设置适当的 minConvexity
和 maxConvexity
(两值在[0, 1]范围)。 filterByInertia=1
(或True),然后设置适当的 minInertiRatio
和 maxInertiRatio
(两值在[0, 1]范围)。 cv2.SimpleBlobDeterctor
参数
# 作者:水果好好吃哦
# 日期:2023/6/30
# 导入库
import cv2
import numpy as np
# 读取图像
im = cv2.imread("image\\blob.jpg", cv2.IMREAD_GRAYSCALE)
# 设置SimpleBlobDetector参数
params = cv2.SimpleBlobDetector_Params()
# 改变阈值
params.minThreshold = 10
params.maxThreshold = 200
# 根据面积过滤
params.filterByArea = True
params.minArea = 1500
# 根据Circularity过滤
params.filterByCircularity = True
params.minCircularity = 0.1
# 根据Convexity过滤
params.filterByConvexity = True
params.minConvexity = 0.87
# 根据Inertia过滤
params.filterByInertia = True
params.minInertiaRatio = 0.01
# 创建一个带有参数的检测器
# opencv3.0前的版本使用cv2.SimpleBlobDetector()创建检测器
# 3.0之后的版本使用cv2.SimpleBlobDetector_create()创建
ver = cv2.__version__.split('.')
if int(ver[0]) < 3:
detector = cv2.SimpleBlobDetector(params)
else:
detector = cv2.SimpleBlobDetector_create(params)
# 检测blobs
keypoints = detector.detect(im)
# 用红色圆圈画出检测到的blobs
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS 确保圆的大小对应于blob的大小
im_with_keypoints = cv2.drawKeypoints(im, keypoints, np.array([]), (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# 结果显示
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)
运行结果如下: 这里需要补充一个函数, cv2.drawKeypoints() ,即绘制特征关键点函数。 语法:cv2.drawKeypoints(image, keypoints, outImage[, color[, flags]])-->outImage 参数: image---源图像。 keypoints---特征点向量,向量内每个元素是一个keypoint对象,它包含了特征点的各种属性信息。 outImage---特征点绘制的画布对象,可以是原始图像。 color---绘制的特征点的颜色信息,默认绘制的是随机彩色。 flags---特征点的绘制模式,有以下几种模式可选。 :::info ① DRAW_MATCHES_FLAGS_DEFAULT ,只绘制特征点的坐标点,显示在图像上就是一个个小圆点,每个圆点的坐标都是特征点的坐标。② DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG ,函数不创建输出的图像,而是直接在输出图像变量空间绘制,要求本身输出图像变量就是一个初始化好了的,size与type都是已经初始化好的变量。③ DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS ,单点的特征点不被绘制。④ DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ,绘制特征点的时候绘制的是一个个带有方向的圆,这种方法同时显示图像的坐标,size和方向,是最能显示特征的一种绘制方式。 ::
边缘检测是一种重要的图像处理技术,用于确定图片中物体的边界(边缘)或区域。边缘检测算法广泛应用于生产工作中的计算机视觉算法处理流水线中。 在进行检测前需要进行预处理,①边缘检测主要是依靠图像的像素强度来判别是否为边缘,因此需要将图像转为灰度图。②在对图像进行边缘检测之前需要进行平滑处理,以减少图像中的噪点。这是因为在边缘检测中,需要计算像素强度的数值导数,而噪点会产生不必要的干扰。 这里我们介绍两种重要的边缘检测算法:Sobel边缘检测和Canny边缘检测.
关于Sobel检测的原理,可参看 Edge Detection Using OpenCV | LearnOpenCV # 语法:cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])--->dst 参数: src---源图像。ddepth---输出图像的位深度。 dx---x方向的求导阶数。dy---y方向的求导阶数。 ksize---Sobel卷积核的尺寸,必须是奇数.
import cv2
image = cv2.imread('image\\tiger.png', cv2.IMREAD_GRAYSCALE)
# 平滑图像
image_blur = cv2.GaussianBlur(image, (3, 3), sigmaX=1, sigmaY=0)
# Sobel算子1,dx=1,dy=0
sobel_x = cv2.Sobel(image_blur, cv2.CV_64F, dx=1, dy=0, ksize=3)
# Sobel算子2,dx=0,dy=1
sobel_y = cv2.Sobel(image_blur, cv2.CV_64F, dx=0, dy=1, ksize=3)
# Sobel算子3,dx=1,dy=1
sobel_xy = cv2.Sobel(image_blur, cv2.CV_64F, dx=1, dy=1, ksize=3)
cv2.imshow('Sobel X', cv2.resize(sobel_x, None, fx=0.6, fy=0.6))
cv2.waitKey(0)
cv2.imshow('Sobel Y', cv2.resize(sobel_y, None, fx=0.6, fy=0.6))
cv2.waitKey(0)
cv2.imshow('Sobel XY', cv2.resize(sobel_xy, None, fx=0.6, fy=0.6))
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
关于Canny检测的原理,可参看 Edge Detection Using OpenCV | LearnOpenCV # 语法:cv2.Canny(image, threshold1, threshold2[, edges[, apertureSie[, L2gradient]]]])--->dst 参数: image---8位的图像。 threshold1---阈值1,强度小于此阈值的像素将被排除。 threshold2---阈值2,强度大于此阈值的像素将于实体边相关联.
import cv2
image = cv2.imread('image\\tiger.png', 0)
# 平滑图像
image_blur = cv2.GaussianBlur(image, (3, 3), sigmaX=1, sigmaY=0)
edges = cv2.Canny(image_blur, 75, 200)
cv2.imshow('Canny Edge Detection', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
轮廓检测可以检测物体的边界,并在图像中轻松定位它们。它可以被应用到图像前景提取、简单图像分割、检测和识别等工作中。 等高线:指具有相同颜色和强度的边界像素点连接而成的闭合线路,也就是轮廓。 在opencv中,我们可以使用 cv2.findContours() 和 cv2.drawContours() 在图像中查找和绘制轮廓。在查找轮廓时,常用两种检测算法: cv2.CHAIN_APPROX_NONE 、 cv2.CHAIN_APPROX_SIMPLE .
cv2.findContours()
函数检测图像轮廓。 cv2.drawContours()
函数绘制图像轮廓。 首先我们先来了解两个函数, cv2.findContours() 和 cv2.drawContours() .
cv2.findContours() 语法:cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])--->contours, hierarchy 参数: image---二值图像。 mode---轮廓检索模式。包括 cv2.RETR_EXTERNAL 、 cv2.RETR_LIST 、 cv2.RETR_CCOMP 、 cv2.RETR_TREE 。示例中,我们将使用 cv2.RETR_TREE ,这意味着将从二值图像中检索所有可能的轮廓。 method---轮廓近似方法。常用 cv2.CHAIN_APPROX_NONE 和 cv2.CHAIN_APPROX_SIMPLE 。 cv2.CHAIN_APPROX_NONE 存储所有的等高点; cv2.CHAIN_APPROX_SIMPLE 压缩水平、垂直和对角线段,仅保留其端点。例如,举行轮廓使用4个角点进行编码。 hierarchy---层次结构。 offset---偏移。 返回:contours---轮廓线。hierarchy---层次结构.
cv2.drawContours() 语法:cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])--->image 参数: image---要在其上绘制轮廓的图像。 contours---从cv2.findContours()函数获得的轮廓线。 contoursIdx---等值线点的像素坐标列在获得的等值线中。使用此参数,可以指定此列表中的索引位置,以准确指示要绘制的轮廓点。提供负值将绘制所有等值线点。 color---要绘制的轮廓线的颜色。 thickness---要绘制的轮廓线的粗细,若为负数则填充轮廓线内部.
import cv2
apple = cv2.imread('image\\apple.jpg')
image = cv2.cvtColor(apple, cv2.COLOR_BGR2GRAY)
image_copy = apple.copy()
retval, dst = cv2.threshold(image, 100, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(dst, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=2)
cv2.imshow('111', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下:
import cv2
apple = cv2.imread('image\\apple.jpg')
image = cv2.cvtColor(apple, cv2.COLOR_BGR2GRAY)
image_copy = apple.copy()
retval, dst = cv2.threshold(image, 100, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(dst, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=2)
cv2.imshow('111', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下: 可以看到 cv2.CHAIN_APPROX_NONE 和 cv2.CHAIN_APPROX_SIMPLE 的输出之间几乎没有差异。这是为什么呢? 这归功于 cv2.drawContours() 函数。尽管 cv2.CHAIN_APPROX_SIMPLE 方法通常生成的点较少,但 cv2.drawContours() 函数会自动连接相邻的点,即使它们不在轮廓列表中,也会连接它们.
层次结构(hierarchy)表示等高线(轮廓线)之间的父子关系,在轮廓检测中轮廓检索模式mode对生成的轮廓层次结构产生影响。 为什么要引入层次结构概念呢?这是因为,我们既需要描绘图像中单个对象的轮廓,也需要描绘对象和其内部的轮廓。 通过上图中的几个形状和线条我们可以很好的了解层次结构,不同的数字显示了不同形状之间的层次结构.
cv2.findContours()函数返回的hierarchy(层次结构)是一个三维数组,该数组中的每个层次结构包含了四个值,分别是Next, Previous, First_Child, Parent。 Next---表示当前轮廓的下一个处于同一层次结构级别的轮廓索引。上图中,等高线1与2的结构级别处于同一层次结构级别上,因此1 Next---> 2;没有与等高线3处于同一层次结构级别的等高线,因此3 Next---> -1。 Previous---表示当前轮廓的上一个处于同一层次结构级别的轮廓索引。 First_Child---表示当前轮廓的第一个子轮廓索引。上图中等高线3的子轮廓是3a,因此3 First_Child---> 3a。 Parent---表示当前轮廓的父等高线的索引.
不同轮廓检索模式下的轮廓层次结构和轮廓线: mode = cv2.RETR_LIST RETR_LIST模式不会在提取的轮廓之间创建任何父子关系,因此,对于检测到的所有等高线的First_Child与Parent的值都是-1.
import cv2
image2 = cv2.imread('image\\shape.jpg')
image = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
retval, thresh2 = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
contours5, hierarchy5 = cv2.findContours(thresh2, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
image_copy6 = image2.copy()
cv2.drawContours(image_copy6, contours5, -1, (0, 255, 0), 2, cv2.LINE_AA)
cv2.imshow('RETR_LIST', image_copy6)
print(f"RETR_LIST: \n{hierarchy5}")
cv2.waitKey(0)
cv2.imwrite('image\\contours_retr_list.jpg_LIST.jpg', image_copy6)
cv2.destroyAllWindows()
'''
RETR_LIST:
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[ 3 1 -1 -1]
[ 4 2 -1 -1]
[-1 3 -1 -1]]]
'''
生成图如下:
mode = cv2.RETR_EXTERNAL RETR_EXTERNAL模式仅检测父轮廓,并忽略任何子轮廓,因此所有的内部轮廓(如 3a 和 4)都不会被绘制.
import cv2
image2 = cv2.imread('image\\shape.jpg')
image = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
retval, thresh2 = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
contours5, hierarchy5 = cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
image_copy6 = image2.copy()
cv2.drawContours(image_copy6, contours5, -1, (0, 255, 0), 2, cv2.LINE_AA)
cv2.imshow('RETR_EXTERNAL', image_copy6)
print(f"RETR_EXTERNAL: \n{hierarchy5}")
cv2.waitKey(0)
cv2.imwrite('image\\contours_retr_external.jpg', image_copy6)
cv2.destroyAllWindows()
'''
RETR_EXTERNAL:
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[-1 1 -1 -1]]]
'''
生成图如下:
mode = cv2.RETR_CCOMP RETR_CCOMP模式建立了两级层次结构,即轮廓线不是第一级层次结构就是第二级层次结构;若有嵌套的轮廓线,则2N+1(N是非负整数)是第一级层次结构。如下图所示, 。
import cv2
image2 = cv2.imread('image\\shape.jpg')
image = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
retval, thresh2 = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
contours5, hierarchy5 = cv2.findContours(thresh2, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
image_copy6 = image2.copy()
cv2.drawContours(image_copy6, contours5, -1, (0, 255, 0), 2, cv2.LINE_AA)
cv2.imshow('RETR_CCOMP', image_copy6)
print(f"RETR_CCOMP: \n{hierarchy5}")
cv2.waitKey(0)
cv2.imwrite('image\\contours_retr_ccomp.jpg', image_copy6)
cv2.destroyAllWindows()
'''
RETR_CCOMP:
[[[ 1 -1 -1 -1]
[ 3 0 2 -1]
[-1 -1 -1 1]
[ 4 1 -1 -1]
[-1 3 -1 -1]]]
'''
生成图如下:
mode = cv2.RETR_TREE RETR_TREE模式创建完整的层次结构,级别不限于两级,其视情况而定.
import cv2
image2 = cv2.imread('image\\shape.jpg')
image = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
retval, thresh2 = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
contours5, hierarchy5 = cv2.findContours(thresh2, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
image_copy6 = image2.copy()
cv2.drawContours(image_copy6, contours5, -1, (0, 255, 0), 2, cv2.LINE_AA)
cv2.imshow('RETR_TREE', image_copy6)
print(f"RETR_TREE: \n{hierarchy5}")
cv2.waitKey(0)
cv2.imwrite('image\\contours_retr_tree.jpg', image_copy6)
cv2.destroyAllWindows()
'''
RETR_TREE:
[[[ 3 -1 1 -1]
[-1 -1 2 0]
[-1 -1 -1 1]
[ 4 0 -1 -1]
[-1 3 -1 -1]]]
'''
生成图如下:
不同轮廓检索方式的运行时间比较:
轮廓检索方式 | 运行时间 (s) |
---|---|
cv2.RETR_LIST | 0.000382 |
cv2.RETR_EXTERNAL | 0.000554 |
cv2.RETR_CCOMP | 0.001845 |
cv2.RETR_TREE | 0.005594 |
最后此篇关于快速入门OpenCv(python版)的文章就讲到这里了,如果你想了解更多关于快速入门OpenCv(python版)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Hive —— 入门 Hive介绍 Apache Hive是一款建立在Hadoop之上的开源数据仓库系统,可以将存储在Hadoop文件中的结构化、半结构化数据文件映射为一张数据库表,基于表提供了一
HBase —— 入门 HBase介绍 HBase是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”
零:前端目前形势 前端的发展史 HTML(5)、CSS(3)、JavaScript(ES5、ES6):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) ->
在本教程中,您将了解在计算机上运行 JavaScript 的不同方法。 JavaScript 是一种流行的编程语言,具有广泛的应用程序。 JavaScript 以前主要用于使网页具有交
我曾经是一个对编程一窍不通的小白,但因为对互联网世界的好奇心和求知欲的驱使,我踏入了编程的殿堂。在学习的过程中,我发现了一门神奇的编程语言——Python。Python有着简洁、易读的语法,让初学者能
嗨,亲爱的读者们! 今天我要给大家分享一些关于Python爬虫的小案例。你是否曾为了获取特定网页上的数据而烦恼过?或者是否好奇如何从网页中提取信息以供自己使用?那么,这篇文章将会给你一些启示和灵感。
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 8 年前。 Improv
我想创建一个像https://apprtc.appspot.com/?r=04188292这样的应用程序。我对 webrtc 了解一点,但无法掌握 google app-engine。如何为 java
我刚刚开始使用 Python 并编写了一个简单的周边程序。但是,每当我在终端中键入 python perimeter.py 时,都会收到以下错误,我不知道如何解决。 >>> python perime
Redis有5个基本数据结构,string、list、hash、set和zset。它们是日常开发中使用频率非常高应用最为广泛的数据结构,把这5个数据结构都吃透了,你就掌握了Redis应用知识的一半了
创建发布web项目 具体步骤: 1.在开发工具中创建一个dynamic web project helloword 2.在webContent中创建index.html文件 3.发布web应用到
如果你在 Ubuntu 上使用终端的时间很长,你可能会希望调整终端的字体和大小以获取一种良好的体验。 更改字体是一种最简单但最直观的 Linux 的终端自定义 的方法。让我
1. 前言 ADODB 是 Active Data Objects Data Base 的简称,它是一种 PHP 存取数据库的函式组件。现在 SFS3 系统 (校园自由软件交流网学务系统) 计划的
我对 neo4j 完全陌生,我很抱歉提出这样一个基本问题。我已经安装了neo4j,我正在使用shell“localhost:7474/webadmin/#/console/” 我正在寻找一个很好的例子
我正在阅读 ios 4 的核心音频,目的是构建一个小测试应用程序。 在这一点上,我对所有 api 的研究感到非常困惑。理想情况下,我想知道如何从两个 mp3 中提取一些样本到数组中。 然后在回调循环中
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是无关紧要的,因
我下载了 GNUStep并安装了它,但是我不确定在哪里可以找到 IDE。有谁知道什么程序可以用作 GNUStep IDE/从哪里获取它们?否则,有没有人知道有关如何创建和编译基本 GNUStep 程序
我正在尝试开始使用 Apache Solr,但有些事情我不清楚。通读tutorial ,我已经设置了一个正在运行的 Solr 实例。我感到困惑的是 Solr 的所有配置(架构等)都是 XML 格式的。
请问有没有关于如何开始使用 BruTile 的文档? 我目前正在使用 SharpMap,我需要预缓存切片以加快进程 最佳答案 我今天正在研究这个:)Mapsui项目site严重依赖 SharpMap
尽我所能,我无法让 CEDET 做任何事情。 Emacs 24.3。我下载了最新的 CEDET 快照。我从他的底部(不是这样)Gentle Introduction 中获取了 Alex Ott 的设置
我是一名优秀的程序员,十分优秀!