前言:本文是我在学习opencv时记录的笔记,内容较为简洁,会记录从入门到做项目这段时间的内容,最终目的是完成我的毕业设计,欢迎大家给予批评指正。本篇为第一本书《Python-OpenCV从入门到精通》的笔记。
1.线段的绘制
img = cv2.line(img, pt1, pt2, color, thickness)
img:画布;pt1:线段起点坐标;pt2:线段终点坐标;color:线条颜色;thickness:线条宽度。
线条颜色为BGR格式,如(0,0,255)代表红色。
canvas = cv2.line(canvas, (50, 50), (250, 50), (255, 0, 0), 5)
2.矩形的绘制
img = cv2.rectangle(img, pt1, pt2, color, thickness)
当thickness = -1时,为实心矩形。
canvas = cv2.rectangle(canvas, (100, 100), (200, 200), (0, 255, 0), -1)
3.圆形的绘制
img = cv2.circle(img, center, radius, color, thickness)
center:圆心坐标;radius:半径;当thickness = -1时,为实心圆。
import cv2, numpy
#随机绘制27个实心圆
canvas = numpy.zeros((300,300,3),numpy.uint8)#创建画布
for i in range(0,27):
X_radius = numpy.random.randint(0,300)
Y_radius = numpy.random.randint(0,300)
radius = numpy.random.randint(11,70)
color = numpy.random.randint(256,size=(3,)).tolist()#将数组转换为列表
canvas = cv2.circle(canvas, (X_radius, Y_radius), radius, color, -1)
cv2.imshow("canvas",canvas)
cv2.waitKey()
cv2.destroyAllWindows()
4.多边形的绘制
img = cv2.polylines(img, pts, isClosed, color, thickness)
pts:由多边形各个顶点的坐标组成的一个列表,这个列表是一个numpy的数组类型;
isClosed:如果值为True,表示一个闭合的多边形;如果值为False,表示不闭合。
pts = np.array([[100, 50], [200, 50], [250, 250], [50, 250]], np.int32)
canvas = cv2.polylines(canvas, [pts], True, (0, 255, 255), 5)
5.文字的绘制
img = cv2.putText(img, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin)
text:文字内容;org:文字在画布中左下角坐标;
fontFace:字体样式;fontScale:字体大小;
lineType:线型(有4和8两个值,默认为8);
bottomLeftOrigin:绘制文字时的方向(True,False,默认为False)
字体样式 | 含义 |
---|---|
FONT_HERSHEY_SIMPLEX | 正常大小的sans-serif字体 |
FONT_HERSHEY_ PLAIN | 小号的sans-serif字体 |
FONT_HERSHEY_ DUPLEX | 正常大小的sans-serif字体(更复杂) |
FONT_HERSHEY_ COMPLEX | 正常大小的serif字体 |
FONT_HERSHEY_TRIPLEX | 正常大小的serif字体(更复杂) |
FONT_HERSHEY_ COMPLEX_SMALL | FONT_HERSHEY_ COMPLEX字体样式的简化版 |
FONT_HERSHEY_ SCRIPT_SIMPLEX | 手写风格的字体 |
FONT_HERSHEY_SCRIPT_COMPLEX | FONT_HERSHEY_ SCRIPT_SIMPLEX字体的进阶版 |
FONT_HERSHEY_ ITALIC | 斜体 |
canvas = cv2.putText(canvas, "HELLO", (50, 250), cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 0, 0), 5)
6.动态绘制图形
例:弹球动画
#弹球动画
import cv2, time
import numpy as np
width, height = 200, 200 #画布宽和高为200
r = 20 #圆半径
x = r + 20 #圆心横坐标起始坐标
y = r + 100 #圆心纵坐标起始坐标
x_offer = y_offer = 4 #每帧的移动速度
while cv2.waitKey(1) == -1: #按下任意按键结束
if x > width - r or x < r: #如果坐标触碰边界
x_offer *= -1 #换向
if y > height - r or y < r:
y_offer *= -1
x += x_offer #移动圆心
y += y_offer
canvas = np.ones((width,height,3), np.uint8) * 255 #创建画布
canvas = cv2.circle(canvas, (x, y), r, (0, 255, 0), -1) #绘制圆形
cv2.imshow("img", canvas) #显示图像
time.sleep(1/60)#休眠1/60s,实现每秒60帧
cv2.destroyAllWindows()
dst = cv2.resize(src, dsize, fx, fy, interpolation)
src:原始图像;dsize:输出图像的大小,格式为元组(宽,高),单位为像素;
fx:水平方向的缩放比例;fy:垂直方向的缩放比例;
interpolation:缩放的插值方式,建议使用默认值;
dst:缩放后的图像。
(1)dsize参数实现缩放
使用dsize,可以不使用fx、fy。
dst1 = cv2.resize(img, (100, 100))
(2)fx、fy参数实现缩放
dsize参数必须使用None,否则fx、fy失效。
fx、fy参数可以使用浮点值,小于1的值表示缩小,大于1的值表示放大,计算公式为:
新图像宽度 = round(fx × 原图像宽度)
新图像高度 = round(fy × 原图像高度)
dst2 = cv2.resize(img, None, fx=1 / 3, fy=1 / 2)
水平方向称为X轴,垂直方向称为Y轴。
dst = cv2.flip(src, flipCode)
flipCode:翻转类型。
类型值 | 含义 |
---|---|
0 | 沿X轴翻转 |
正数 | 沿Y轴翻转 |
负数 | 同时沿X轴、Y轴翻转 |
dst3 = cv2.flip(img, -1)
仿射变换是一种在二维平面中发生的几何变形,变换之后的图像仍可以保持直线的“平直性”和“平行性”,常见的仿射变换包含平移、旋转和倾斜。
dst = cv2.warpAffine(src, M, dsize, flags, borderMode, borderValue)
M:一个2行3列的矩阵,根据此矩阵的值变换原图中的像素位置。
dsize:输出图像的尺寸大小;
flags:插值方式,建议使用默认值;
borderMode:边界类型,建议使用默认值;
borderValue:边界值,默认为0,建议使用默认值。
其中:
M被叫做仿射矩阵,实际上是一个2×3的列表,格式:
M = [[a, b, c], [d, e, f]]
图像作何种仿射变换取决于M的值,仿射变换的输出图像按照以下公式计算:
新x = 原x × a + 原y × b + c
新y = 原x × d + 原y × e + f
M矩阵中的数字采用32位浮点格式,可采用两种方式创建M:
import numpy as np
# 创建一个全是0的M
M = np.array((2,3),np.uint32)
# 创建M的同时赋予具体值
M = np.float32([[1, 2, 3], [4, 5, 6]])
1.平移
M = [[1, 0, 水平移动的距离], [0, 1, 垂直移动的距离]]
rows = len(img) # 图像像素行数
cols = len(img[0]) # 图像像素列数
M = np.float32([[1, 0, 100], [0, 1, 50]]) # 向右下方平移
dst = cv2.warpAffine(img, M, (cols, rows))
2.旋转
M = cv2.getRotationMatrix2D(center, angle, scale)
center:旋转的中心点坐标;
angle:旋转的角度,正数表示逆时针,负数表示顺时针;
scale:缩放比例,浮点类型。
# 旋转
M2 = cv2.getRotationMatrix2D((rows/2, cols/2), -30, 1)
dst2 = cv2.warpAffine(img, M2, (cols, rows))
3.倾斜
M = cv2.getAffineTransform(src, dst)
src:原图三个点(左上角A,右上角B,左下角C)坐标,格式位3行2列的32位浮点数列表;
dst:倾斜图像的三个点坐标。
# 倾斜
p1 = np.zeros((3, 2), np.float32)
p1[0] = [0, 0]
p1[1] = [cols - 1, 0]
p1[2] = [0, rows - 1]
p2 = np.zeros((3, 2), np.float32)
p2[0] = [50, 0]
p2[1] = [cols - 1, 0]
p2[2] = [0, rows - 1]
M3 = cv2.getAffineTransform(p1, p2)
dst3 = cv2.warpAffine(img, M3, (cols, rows))
如同在不同的角度去观察物品,透视就是让图像在三维空间中变形,变形后的画面就是透视图。
OpenCV通过定位图像的4个点计算透视。
dst = cv2.warpPerspective(src, M, dsize, flags, borderMode, borderValue)
src:原始图像;M:3×3矩阵;dsize:输出图像尺寸。
M = cv2.getPerspectiveTransform(src, dst)
src:原图4个点坐标,格式位4行2列的32位浮点数列表;
dst:透视图的4个点坐标。
# 模拟从底部观察图像得到的透视效果
import cv2
import numpy as np
img = cv2.imread("D:/1a.study/opencv/Python OpenCV/sl/7/01/demo.png")
rows = len(img) # 图像像素行数
cols = len(img[0]) # 图像像素列数
# 原图像坐标
p1 = np.zeros((4,2), np.float32)
p1[0] = [0, 0]
p1[1] = [cols - 1, 0]
p1[2] = [0, rows - 1]
p1[3] = [cols - 1, rows - 1]
# 透视图坐标
p2 = np.zeros((4,2), np.float32)
p2[0] = [90, 0]
p2[1] = [cols - 90, 0]
p2[2] = [0, rows - 1]
p2[3] = [cols - 1, rows - 1]
# 计算M
M = cv2.getPerspectiveTransform(p1, p2)
dst = cv2.warpPerspective(img, M, (cols, rows))
cv2.imshow("dst",dst)
cv2.waitKey()
cv2.destroyAllWindows()
retval, dst = cv2.threshold(src, thresh, maxval, type)
src:被处理的图像;
thresh:阈值;
maxval:阈值处理采用的最大值;
type:阈值处理类型;
类型 | 含义 | 作用 |
---|---|---|
cv2.THRESH_BINARY | 二值化阈值处理 | 使灰度图像的像素值两极分化,非黑即白 |
cv2.THRESH_ BINARY_INV | 反二值化阈值处理 | |
cv2.THRESH_TOZERO | 低于阈值零处理 | 将一个范围内的像素值变为0,范围外保留 |
cv2.THRESH_TOZERO_INV | 超出阈值零处理 | |
cv2.THRESH_TRUNC | 截断阈值处理 | 将大于阈值的像素值变为和阈值一样的值 |
retval:处理时采用的阈值;
dst:处理后的图像。
1.二值化处理
二值化处理时,大于阈值的像素值变为最大值,小于等于阈值的像素值变为0
t1, dst1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
1.低于阈值零处理
将低于或等于阈值的像素值变为0,大于的像素值保留(将深区域的变黑)。
2.超出阈值零处理
将大于阈值的像素值变为0,小于等于的保留(将浅区域的变黑)。
将大于阈值的像素值变为和阈值一样的值,小于等于阈值的像素值保留原值。
自适应阈值是根据图像中某一正方形区域内的所有像素值按照指定的算法计算得到的,能更好处理明暗分布不均的图像。
dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C )
src:被处理的图像,须为灰度图像;
maxValue:阈值处理采用的最大值;
adaptiveMethod:自适应阈值的计算方法:
计算方法 | 含义 |
---|---|
cv2.ADAPTIVE_THRESH_MEAN_C | 对一个正方形区域内的所有像素平均加权 |
cv2.ADAPTIVE_THRESH_GAUSSIAN_C | 根据高斯函数按照像素与中心点的距离对一个正方形区域内的所有像素进行加权计算 |
thresholdType:阈值处理类型,二值化或反二值化;
blockSize:一个正方形区域的边长;
C:常量,阈值等于均值或者加权值减去这个常量。
dst1 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 5, 3)
用于找到最合适的阈值。在为type传递参数时多传递一个参数:cv2.THRESH_OTSU,且把阈值设置为0。
t1, dst1 = cv2.threshold(img_col, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
覆盖原始图像,仅暴露感兴趣区域(ROI)的模板图像叫做掩模。
# 创建感兴趣区域横坐标20、纵坐标50,宽60、高50的矩形掩模图像
mask = np.zeros((150, 150, 3), np.uint8)
mask[50:100, 20:80, :] = 255
相同位置的像素值相加组成了新图像。
dst = cv2.add(src1, src2, mask, dtype)
src1:图像1;src2:图像2;
mask:掩模,使用默认值;
dtype:图像深度,使用默认值。
如果使用“+”进行加法运算,计算值超过了255,就会取相加和除以255的余数,即取模运算。
使用add进行加法运算,如果计算值超过255,取255。
mask = np.zeros((rows, cols, 1), np.uint8)
sum1 = img + img
sum2 = cv2.add(img, img, mask=mask)
位运算方法 | 含义 |
---|---|
cv2.bitwise_and | 按位与 |
cv2.bitwise_or | 按位或 |
cv2.bitwise_not | 按位取反 |
cv2.bitwise_xor | 按位异或 |
1.按位与
dst = cv2.bitwise_and(src1, src2, mask)
某像素与纯白像素与运算,结果为原像素;
某像素与纯黑像素与运算,结果为纯黑像素。
故原图像与掩模与运算,原图像仅保留掩模中白色区域覆盖的内容。
img1 = cv2.bitwise_and(img, mask)
2.按位或
dst = cv2.bitwise_or(src1, src2, mask)
某像素与纯白像素或运算,结果为纯白像素;
某像素与纯黑像素或运算,结果为原像素。
故原图像与掩模与运算,原图像仅保留掩模中黑色区域覆盖的内容。
3.按位取反
dst = cv2.bitwise_not(src, mask)
图像经过取反运算后颜色与原图像完全相反。
4.按位异或
dst = cv2.bitwise_xor(src, mask)
某像素与纯白像素异或,结果为原像素的取反;
某像素与纯黑像素异或,结果为像素的原值;
故原图与掩模异或,掩模白色区域取反,黑色区域不变。
对异或结果再异或会还原成最初的值。
# 图像加密、解密
import cv2
import numpy as np
# 异或运算
def encode(img, img_key):
result = img = cv2.bitwise_xor(img, img_key)
return result
# 读取原始图片
pic = cv2.imread("D:/1a.study/opencv/Python OpenCV/sl/7/01/demo.png")
cv2.imshow("img", pic)
# 创建随机像素的掩模
rows, cols, channel = pic.shape
img_key = np.random.randint(0, 256, (rows, cols, 3), np.uint8)
cv2.imshow("mask",img_key)
# 加密
img_result = encode(pic, img_key)
cv2.imshow("result1", img_result)
#解密
img_result = encode(img_result, img_key)
cv2.imshow("result2", img_result)
cv2.waitKey()
cv2.destroyAllWindows()
1.加权和
在尽量保留原图像信息的基础上把两幅图像融合到一起。
dst = cv2.addWeighted(src1, alpha, src2, beta, gamma)
src1:第一幅图像;alpha:第一幅图像的权重;
src2:第二幅图像;beta:第二幅图像的权重;
gamma:在和结果上添加的标量,值越大图像越亮,可以是负数。
import cv2
# 读取原始图像
img1 = cv2.imread("D:/1a.study/opencv/Python OpenCV/sl/9/10/sunset.jpg")
img2 = cv2.imread("D:/1a.study/opencv/Python OpenCV/sl/9/10/beach.jpg")
cv2.imshow("img1", img1)
cv2.imshow("img2", img2)
# 对第二幅图像缩放成第一幅图像的大小
rows, colmns, channel = img1.shape
img2 = cv2.resize(img2, (colmns, rows))
# 进行加权和
img3 = cv2.addWeighted(img1, 0.6, img2, 0.6, 0)
cv2.imshow("img3", img3)
cv2.waitKey()
cv2.destroyAllWindows()
2.覆盖
从图像A中取像素值, 直接赋给图像B的像素。