OpenCv基础(二)
文章目录
三、图像运算和基本变换
-
OpenCv用mat这种数据结构来表示图片
-
但在python中,把mat转化为了numpy的ndarray数据格式
-
ndarray数据格式的主要属性:size,shape,dtype
1.1 拷贝
- 深拷贝
img.copy
、浅拷贝img.view
import cv2
import numpy as np
img = cv2.imread('../my_work/01.png')
# 浅拷贝
img2 = img.view()
# 深拷贝
img3 = img.copy()
img[10:100,10:100] = [0,0,255]
cv2.imshow('img',np.hstack((img,img2,img3))) # 横着将几张图片拼接在一起
# cv2.imshow('img',np.vstack((img,img2,img3))) # # 竖着将几张图片拼接在一起
cv2.waitKey(0)
cv2.destroyAllWindows()
1.2 图像的分割与融合
split(img):
img:
待分割图像
merge(mv):
mv:
合并图像数组
import cv2
import numpy as np
img = np.zeros((200,200,3),np.uint8)
# 分割通道,分成rgb三个通道
b,g,r = cv2.split(img)
# 修改指定通道的部分区域颜色
b[10:100,10:100] = 255
g[10:100,10:100] = 255
# 再合并通道
img2 = cv2.merge((b,g,r))
cv2.imshow("img",np.hstack((img,img2)))
cv2.waitKey(0)
cv2.destroyAllWindows()
1.3 绘制图形
-
绘制直线:
cv2.line(img,pt1,pt2,color,thickness,lineType,shift)
img:
绘制在img图像上pt1:
起始点pt2:
终止点color:
线条颜色thickness:
线条粗细,值 = -1时表示填充lineType:
线条类型shift:
缩放参数
-
绘制矩形框:
cv2.rectangle()参数同上
-
绘制圆形:
cv2.circle(img,center,radius,color[,thickness,lineType,shift])
center:
中心点radius:
半径
-
绘制椭圆:
cv2.ellipse(img,center,axes,angle,startAngle,endAngle.....)
axes:
长轴及短轴的一半angle:
椭圆旋转的角度startAngle:
绘制椭圆起始角度endAngle:
绘制椭圆终止角度
-
多边形:
cv2.polylines(img,pts,isClosed,color,thickness)
pts:
为多边形的点集,必须为int32类型isClosed:
是否闭合,值为bool类型
-
填充多边形:
cv2.fillPoly(img,pts,color,thickness)
-
绘制文本:
cv2.putText(img,str,org,fontFace,fontScale,color)
str:
绘制的文字org:
文字起始坐标fontFace:
文字格式fontScale:
文字大小
代码示例:
import cv2
import numpy as np
img = np.zeros((640,480,3),np.uint8)
# 直线
cv2.line(img,(10,20),(300,400),(0,0,255),5,4)
cv2.line(img,(80,100),(380,480),(0,0,255),5,16) # 有锯齿状
img2 = np.zeros((640,480,3),np.uint8)
# 矩形
cv2.rectangle(img2,(10,20),(250,460),(0,255,0),5,4)
# 圆
cv2.circle(img2,(240,320),150,(0,255,0),4)
# 椭圆
cv2.ellipse(img2,(240,320),(100,50),0,0,360,[0,0,255])
# 多边形
# pts多边形的点集,必须是int32位
pts = np.array([[(300,10),(150,100),(50,100)]],np.int32)
cv2.polylines(img2,pts,True,(0,0,255),4)
# 填充多边形
cv2.fillPoly(img2,pts,(0,0,255),4)
# 绘制文本,opencv不能绘制中文字体,可以使用pillow包
cv2.putText(img2,'love',(100,120),cv2.FONT_HERSHEY_COMPLEX,3,(0,0,255))
cv2.imshow('draw2',img2)
cv2.imshow('draw',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 使用pillow绘制中文
from PIL import ImageFont,ImageDraw,Image
img = np.full((500,500,3),fill_value=255,dtype=np.uint8)
font = ImageFont.truetype('下载的字体格式路径')
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)
draw.text((100,250),"我爱你",font=font,fill=(0,255,0,0))
img = np.array(img_pil)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.4 图像的基本运算
- 加法:
cv2.add(img1,img2)
,像素相加超过255,会变成255,也就是白色- 图片还能与单个数字进行运算,每个与100进行加法运算,超出255会被截断,相当于%255
- 减法:
cv2.subtract(img1,img2)
,减完小于0,变为0 - 乘法:
cv2.multiply(img1,img2)
,超过255,会变成255 - 除法:
cv2.divide(img1,img2)
,小于0,变为0
import cv2
img1 = cv2.imread('../my_work/01.png')
img2 = cv2.imread('../my_work/02.png')
# 将尺寸变为一致
new_img1 = cv2.resize(img1,(img2.shape[:-1][::-1]))
# 加法,两个图片的尺寸相同,维数相同
img_add = cv2.add(img1,img2) # 像素相加超过255,会变成255,也就是白色
# 图片还能与单个数字进行运算,每个与100进行加法运算,超出255会被截断,相当于%255
img3 = img2+100
# 减法,减完小于0,变为0
img4 = cv2.subtract(new_img1,img2)
# 乘法
img5 = cv2.multiply(img1,img1)
# 除法
img6 = cv2.divide(img1,img2)
cv2.imshow('img1_img2',cv2.hconcat((new_img1,img2)))
cv2.imshow('add',img_add)
cv2.imshow('img+n',img3)
cv2.imshow('subtract',img4)
cv2.imshow('img1*img2',img5)
cv2.imshow('img1/img2',img6)
cv2.waitKey()
cv2.destroyAllWindows()
1.5 图像的加法融合
- 相当于拿图片做了线性运算,
new_img = img1*w1 + img2*w2 + bias
- w1、w2分别为img1和img2所占权重,bias为常数偏置
import cv2
import numpy as np
# 图像融合相当于拿图片做了线性运算,new_img = img1*w1 + img2*w2 + bias
img1 = cv2.imread('../my_work/01.png')
img2 = cv2.imread('../my_work/02.png')
# 将尺寸变为一致
new_img1 = cv2.resize(img1,(img2.shape[:-1][::-1]))
concat = cv2.addWeighted(new_img1,1.2,img2,0.5,-100)
cv2.imshow('img1_img2',cv2.hconcat((new_img1,img2)))
cv2.imshow('concat',concat)
cv2.waitKey()
cv2.destroyAllWindows()
1.6 位运算
-
OpenCv中的逻辑运算—与或非、异或
- 也就是对应位置元素进行与或非、异或;即对应像素的灰度值转化为二进制进行位运算,再转化为灰度值
- 0反过来就是255
-
非运算:相当于255-img
cv2.bitwise_and(img1,img2)
-
或运算:
cv2.bitwise_or(img1,img2)
-
异或运算:
cv2.bitwise_xor(img1,img2)
import cv2
import numpy as np
img1 = cv2.imread('../my_work/01.png')
img2 = cv2.imread('../my_work/02.png')
# 非运算,相当于255-img
img1_not = cv2.bitwise_not(img1)
# 与运算,形状要求相同
img1_and_img2 = cv2.bitwise_and(img1,img2)
print(img1[:2,:2])
print(img2[:2,:2])
print(img1_and_img2[:2,:2])
# 或运算
img1_or_img2 = cv2.bitwise_or(img1,img2)
# 异或运算
img1_xor_img2 = cv2.bitwise_xor(img1,img2)
cv2.imshow('not',np.hstack((img1,img1_not)))
cv2.imshow('and',np.hstack((img1,img1_and_img2,img2)))
cv2.imshow('or',np.hstack((img1,img1_or_img2,img2)))
cv2.imshow('xor',np.hstack((img1,img1_xor_img2,img2)))
cv2.waitKey()
cv2.destroyAllWindows()
1.7 resize操作
cv2.resize(src,dsize[,dst[,fx,[,fy[,interpolation]]]])
src:
要缩放的图片dsize:
缩放之后的大小,元组和列表表示均可,先宽后高(w,h)dst:
可选参数,缩放之后的输出图片fx,fy:
x轴和y轴的缩放比,即宽度和高度的缩放比interpolation:
插值算法,主要为:INTER_NEAREST
,邻近插值,速度快、效果差INTER_LINEAR
,双线性插值,默认,需要用原图4个点INTER_CUBIC
,三次插值,需要用原图16个点INTER_AREA
,区域插值,效果最好、计算时间长
import cv2
import numpy as np
# resize(src,dsize[,dst[,fx,[,fy[,interpolation]]]])
img1 = cv2.imread('../my_work/01.png')
img2 = cv2.imread('../my_work/02.png')
# resize是先宽后高
img1_new = cv2.resize(img1,(img2.shape[:-1][::-1]))
print(img2.shape)
print(img2.shape[:-1])
print(img2.shape[:-1][::-1])
cv2.imshow('img1_new',cv2.hconcat((img1_new,img1)))
cv2.waitKey()
cv2.destroyAllWindows()
1.8 图像的旋转
-
翻转
cv2.flip(src,flipCode)
flipCode = 0
表示上下翻转flipCode > 0
表示左右翻转flipCode < 0
表示上下左右翻转
-
旋转
cv2.rotate(src,rotateCode)
rotateCode = cv2.ROTATE_90_CLOCKWISE
表示90°顺时针旋转rotateCode = cv2.ROTATE_180
表示180°顺时针旋转rotateCode = cv2.ROTATE_90_COUNTERCLOCKWISE
表示90°逆时针旋转
import cv2
img1 = cv2.imread('../my_work/01.png')
img2 = cv2.imread('../my_work/02.png')
img1_new = cv2.flip(img1,0)
img2_new = cv2.rotate(img2,cv2.ROTATE_90_CLOCKWISE)
cv2.imshow('img1_flip',cv2.hconcat((img1_new,img1)))
# 旋转90度后宽高不一致
cv2.imshow('img2_rotate',img2_new)
cv2.imshow('img2',img2)
cv2.waitKey()
cv2.destroyAllWindows()
1.9 图像的仿射变换—图像平移
-
仿射变换是图像旋转、缩放、平移的总称。具体做法是通过一个变换矩阵和原图片进行计算,得到新的坐标,完成变换,所以仿射变换的关键是变换矩阵的计算。
-
cv2.warpAffine(src,M,dsize,flags,mode,value)
M:
变换矩阵dsize:
输出图片大小flags:
与resize中的插值一致mode:
边界外推法标志value:
填充边界值
-
平移矩阵
- 矩阵中的每个元素由
(x,y)
组成,(x,y)
表示这个像素的坐标,假设沿x轴平移tx,沿y轴平移ty,那么最后得到的坐标为 ( x ˇ (\check{x} (xˇ, y ˘ \breve{y} y˘) = (x+tx,y+ty),用矩阵表示:
- 矩阵中的每个元素由
[ x ˇ y ˘ 1 ] = [ 1 0 t x 0 1 t y 0 0 1 ] [ x y 1 ] \large \begin{bmatrix} \check{x}\\ \breve{y}\\ 1\\ \end{bmatrix} =\begin{bmatrix} 1&0&t_x\\ 0&1&t_y\\ 0&0&1\\ \end{bmatrix} \begin{bmatrix} x\\ y\\ 1\\ \end{bmatrix} xˇy˘1 = 100010txty1 xy1
import cv2
import numpy as np
img1 = cv2.imread('../my_work/01.png')
# 获取原图片的尺寸与通道数
h,w,c = img1.shape
# 定义变换矩阵
M = np.float32([[1,0,100],[0,1,0]])
# OpenCV的图片尺寸是先宽后高
new = cv2.warpAffine(img1,M,(w,h)) # 平移操作
cv2.imshow("new",new)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.10 仿射变换—获取旋转变换矩阵
- OpenCv中提供了计算变换矩阵的API
cv2.getRotationMatrix2D(center,angle,scale)
center:
中心点,以图片的哪个点作为旋转中心angle:
角度,旋转的角度,按逆时针旋转scale:
缩放比例
cv2.getAffineTransform(src[],dst[])
,通过三点可以确定变换后的位置,相当于解方程,三个点对应三个方程,解出偏移参数与旋转角度src:
原目标三个点([ , ],[ , ],[ , ])dst:
变换后三个点([ , ],[ , ],[ , ])
import cv2
img1 = cv2.imread('../my_work/01.png')
# 获取原图片的尺寸与通道数
h,w,c = img1.shape
# 1.获取变换矩阵
M = cv2.getRotationMatrix2D((w/2,h/2),15,1.0)
# 2.获取变换矩阵
src = np.float32([[200,100],[50,100],[40,90]])
dst = np.float32([[200,120],[80,110],[80,50]])
M2 = cv2.getAffineTransform(src,dst)
new = cv2.warpAffine(img1,M,(w,h))
new2 = cv2.warpAffine(img1,M2,(w,h))
cv2.imshow("new",new)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.11 透视变换
-
将一种坐标系变成另一种坐标系。简单来说就是把一张斜图变正
-
warpPerspecctive(img,M,dsize,....)
- 对于透视变换来说,M是一个3*3的矩阵
-
getPerspectiveTransform(src,dst)
获取透视变换矩阵,需要4个点,即图片的4个角- src、dst = ([ , ],[ , ],[ , ],[ , ])
import cv2
import numpy as np
img1 = cv2.imread('../my_work/01.png')
print(img1.shape)
# src是原图的4个坐标,dst是变换后的坐标
src = np.float32([[100,100],[200,100],[0,300],[250,290]])
dst = np.float32([[0,0],[210,0],[0,320],[250,290]])
# 获取变换矩阵
M = cv2.getPerspectiveTransform(src,dst)
# 透视变换
img1_new = cv2.warpPerspective(img1,M,(400,400))
cv2.imshow("new",img1_new)
cv2.waitKey(0)
cv2.destroyAllWindows()
dst = ([ , ],[ , ],[ , ],[ , ])
import cv2
import numpy as np
img1 = cv2.imread('../my_work/01.png')
print(img1.shape)
# src是原图的4个坐标,dst是变换后的坐标
src = np.float32([[100,100],[200,100],[0,300],[250,290]])
dst = np.float32([[0,0],[210,0],[0,320],[250,290]])
# 获取变换矩阵
M = cv2.getPerspectiveTransform(src,dst)
# 透视变换
img1_new = cv2.warpPerspective(img1,M,(400,400))
cv2.imshow("new",img1_new)
cv2.waitKey(0)
cv2.destroyAllWindows()