opencv-python图像处理学习笔记1

读取图片

import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread('1.jpg',0)
# cv.imshow('image',img)
plt.imshow(img)
plt.show()

output_1_0

绘制几何图形

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 创建黑色的图像
img = np.zeros((512, 512, 3), np.uint8)
# 5是线段的粗细
cv.line(img, (0, 0), (511, 400), (102, 124, 50), 5)

# 画圆圈
cv.circle(img, (200, 250), 50, (50, 140, 140),10)

# 
plt.imshow(img[:,:])
plt.show()

png

实现鼠标作为画笔

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
events = [i for i in dir(cv) if 'EVENT' in i] # 一直重复鼠标事件
print(events)
def draw_circle(event, x, y, flags, param):
    if event == cv.EVENT_LBUTTONDBLCLK: # 双击左键,就在对应位置化圆
        cv.circle(img, (x, y), 100, (255, 0, 0), -1)

img = np.zeros((512, 512, 3), np.uint8) # 图片大小
cv.namedWindow('image') # 设置窗口
cv.setMouseCallback('image', draw_circle) # 回调鼠标
while(1): 
    cv.imshow('image', img) # 显示图片  
    if cv.waitKey(20) & 0xFF == 27: # 退出按钮
        break;
cv.destroyAllWindoes() # 销毁窗口

轨迹栏

import numpy as np
import cv2 as cv
def nothing(x):
    pass
# 创建一个黑色的图像,一个窗口
img = np.zeros((300,512,3), np.uint8)
cv.namedWindow('image')
# 创建颜色变化的轨迹栏

cv.createTrackbar('R', 'image', 0, 255, nothing)
cv.createTrackbar('G', 'image', 0, 255, nothing)
cv.createTrackbar('B', 'image', 0, 255, nothing)

# 为on / off 功能创建开关
switch = '0 : OFF \n1: ON' 
cv.createTrackbar(switch, 'image', 0, 1, nothing)
while(1): 
    cv.imshow('image', img)
    k = cv.waitKey(1) & 0xFF # 按下esc按钮 退出调色窗口
    if k == 27:
        break
    r = cv.getTrackbarPos('R', 'image')    
    g = cv.getTrackbarPos('G', 'image')    
    b = cv.getTrackbarPos('B', 'image')
    s = cv.getTrackbarPos(switch, 'image')
    if s == 0:
        img[:] = 0
    else:
        img[:]  = [b, g, r]
cv.destroyAllWindows()

图像的基本操作

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

img = cv.imread('1.jpg') # 加载彩色图像
px = img[100, 100]
print(px) # 该坐标点像素的颜色
blue = img[100, 100, 1] # 仅访问蓝色像素 
#0, 1,2 分别表该像素点对应的蓝色、绿色和红色的值
print(blue)

# 改变像素点的值
img[100, 100] = [255, 200, 100]
print(img[100, 100])

'''
对于单个像素访问,Numpy数组方法array.item()和array.itemset())被认为更好,但是它们始终返回标量。
如果要访问所有B,G,R值,则需要分别调用所有的array.item()
'''
p1 = img.item(10, 10, 2)
print(p1)

img.itemset((10, 10, 2), 255) # 对像素点的值进行修改
print(img.item(10, 10, 2))
[120 107  93]107[255 200 100]68255

访问图像属性

# img.shape()访问,
img = cv.imread('1.jpg')
print(img.shape) 
# 返回行、列和通道数的元组,彩色图->通道数为3print(img.size) # 返回像素点总个数
print(img.dtype) # 数据类型

感兴趣区域ROI

import numpy as np
import cv2 as cv
import matplotlib.pyplot as pltimg = cv.imread('ball.png')
ball = img[280 : 340, 340 : 400]
img[273:333, 100:160] = ball # 复制某个位置的图像到另一个位置
plt.imshow(img)
plt.show()

png

拆分和合并图像通道

#  拆分图像通道
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread('ball.png')
b, g, r = cv.split(img)
print(b)
img = cv.merge((b, g, r))
# 将所有红色像素都设置为0img[:, :, 2] = 0 # 分别表示蓝色通道、绿色通道和红色通道
plt.imshow(img)
plt.show()

图像设置边框

src - 输入图像

top,bottom,left,right 边界宽度(以相应方向上的像素数为单位)

borderType - 定义要添加哪种边框的标志。它可以是以下类型:

1、cv.BORDER_CONSTANT - 添加恒定的彩色边框。该值应作为下一个参数给出。2、cv.BORDER_REFLECT - 边框将是边框元素的镜像

value -边框的颜色,如果边框类型为cv.BORDER_CONSTANT

# 用cv.copyMakeBorder()
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
BLUE = [0,255,0] # 绿色
img1 = cv.imread('opencv-logo.png')
replicate = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REPLICATE)
reflect = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT)
reflect101 = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT_101)
wrap = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_WRAP)
constant=cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_CONSTANT,value=BLUE)
plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
plt.show()

png

图像上的算术运算

'''1、可以通过OpenCV函数 cv.add() 或仅通过numpy操作 res = img1 + img2 添加两个图像2、两个图像应具有相同的深度和类型,或者第二个图像可以只是一个标量值3、OpenCV加法是饱和运算,而Numpy加法是模运算'''
import cv2 as cv
import numpy as np
x = np.uint8([250]) # 250+10 = 260 => 255
y = np.uint8([10]) # 250+10 = 260 % 256 = 4
print(cv.add(x,y))
print(x+y)

图像融合

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img1 = cv.imread('ball.png')
print(img1.size)
img2 = cv.imread('opencv-logo.png')
print(img2.size)
dst = cv.addWeighted(img1,0.7,img2,0.3,0) # 不能运算,因为两幅图大小不一样
plt.imshow(dst)
plt.show()

按位运算

import cv2 as cvimport numpy as npfrom matplotlib import pyplot as plt# 读取两张图片,大小尺寸可以不同
img1 = cv.imread('ball.png')
img2 = cv.imread('opencv-logo.png')
# 创建ROI区域
rows, cols, channels = img2.shape # 读取第二张图片的长宽和通道
roi = img1[0:rows, 0:cols] # roi区域为第二张图片的大小# 创建logo的掩码,并同时创建其相反掩码
img2gray = cv.cvtColor(img2, cv.COLOR_BGR2GRAY) # 将第二张图片转为灰度图像
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY) # 通过阈值处理获得目标图像# ret 是否读到图片, mask: 目标图像
mask_inv = cv.bitwise_not(mask) # bitwise_not是对二进制数据进行“非”操作,即对图像(灰度图像或彩色图像均可)每个像素值进行二进制“非”操作,~1=0,~0=1# 将ROI区域中logo的区域涂黑
img1_bg = cv.bitwise_and(roi, roi, mask = mask_inv)# 仅从logo图像中提取出logo区域
img2_fg = cv.bitwise_and(img2, img2, mask = mask)# 将logo放入ROI并修改图像dst = 
cv.add(img1_bg, img2_fg) # 将处理后的两个图像相加即可
img1[0: rows, 0:cols] = dstplt.imshow(img1)
plt.show()

png

性能衡量和提升技术

'''
除了OpenCV,Python还提供了一个模块time,这有助于衡量执行时间。
另一个模块profile有助于获取有关代码的详细报告
'''

使用opencv衡量性能

cv.getTickCount:函数返回从参考事件(如打开机器的那一刻)到调用此函数那一刻之间的时钟周期数

cv.getTickFrequency:函数返回时钟周期的频率或每秒的时钟周期数

许多 OpenCV 函数都是使用 SSE2、 AVX 等进行优化的

如果启用了 OpenCV,它将运行优化的代码,否则它将运行未优化的代码。

可以使用 cvUseoptimized 检查是否启用 / 禁用和 cvSetuseoptimized 以启用 / 禁用它。

改变颜色空间

# cvtColor(input_image, flag)进行颜色转换,其中flag决定转换的类型'''对于BGR→灰度转换,使用标志cv.COLOR_BGR2GRAY对于BGR→HSV,使用标志cv.COLOR_BGR2HSV'''
import cv2 as cv
flags = [i for i in dir(cv) 
if i.startswith('COLOR_')]
print( flags ) # 打印颜色转换种类

几何变换

cv.warpAffine采用2x3转换矩阵

cv.warpPerspective采用3x3转换矩阵作为输入

OpenCV带有一个函数**cv.resize()。图像的大小可以手动指定,也可以指定缩放比例。也可使用不同的插值方法。

首选的插值方法是cv.INTER_AREA:用于缩小cv.INTER_CUBIC(慢)和cv.INTER_LINEAR用于缩放。
import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.imread('1.jpg', 0)# 图像缩放
res = cv.resize(img, None, fx = 2, fy = 2, interpolation = cv.INTER_CUBIC)
height, width = img.shape[:2]
res1 = cv.resize(img, (3* width, 3 * height), interpolation = cv.INTER_CUBIC)
plt.subplot(221), plt.imshow(res)
plt.subplot(222), plt.imshow(res1)
# 平移图像
'''**cv.warpAffine**函数的第三个参数是输出图像的大小,其形式应为 (width,height) 。
记住width =列数, height =行数'''
rows, cols = img.shape
M = np.float32([[1,0,100],[0,1,50]]) # 偏移为(100,50)
dst = cv.warpAffine(img, M, (cols, rows))
plt.subplot(223), plt.imshow(dst)# 旋转图像
# OpenCV提供了一个函数cv.getRotationMatrix2D# (cols-1)/2.0, (rows - 1)/2.0) 表示中心点
M1 = cv.getRotationMatrix2D(((cols-1)/2.0, (rows - 1)/2.0), 45, 1) # 绕中心旋转45度
dst1 = cv.warpAffine(img, M1, (cols, rows))plt.subplot(224), plt.imshow(dst1)plt.show()

png

仿射变换

在仿射变换中,原始图像中的所有平行线在输出图像中仍将平行。
为了找到变换矩阵,需要输入图像中的三个点及其在输出图像中的对应位置。然后cv.getAffineTransform将创建一个2x3矩阵,该矩阵将传递给cv.warpAffine

import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.imread('1.jpg')rows, cols, ch = img.shape # ch 为通道数# 仿射偏移的位置
pts1 = np.float32([[50, 50],[200, 50], [50, 200]])
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
M = cv.getAffineTransform(pts1, pts2)
dst = cv.warpAffine(img, M, (cols, rows))
plt.subplot(121), plt.imshow(img), plt.title('input')
plt.subplot(122), plt.imshow(dst), plt.title('output')
plt.show()

png

透视变换

函数cv.getPerspectiveTransform找到变换矩阵。
cv.warpPerspective应用于此3x3转换矩阵

import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.imread('sudoku.png')rows, cols, ch = img.shape # ch 为通道数
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M = cv.getPerspectiveTransform(pts1,pts2)
dst = cv.warpPerspective(img,M,(200,200))
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

png

图像阈值

函数cv.threshold用于应用阈值。

第一个参数是源图像,它应该是灰度图像

第二个参数是阈值,用于对像素值进行分类。

第三个参数是分配给超过阈值的像素值的最大值。

OpenCV提供了不同类型的阈值,这由函数的第四个参数给出。

cv.THRESH_BINARY类型。所有简单的阈值类型为:

-cv.THRESH_BINARY

-cv.THRESH_BINARY_INV

-cv.THRESH_TRUNC

-cv.THRESH_TOZERO

-cv.THRESH_TOZERO_INV

该方法返回两个输出,第一个是使用的阈值,第二个输出是阈值后的图像

import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.imread('grident.png',0)# ret是使用的阈值,thresh是返回的处理后的图像
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
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]
for i in range(6):    
	plt.subplot(2,3,i+1),
	plt.imshow(images[i],'gray')    
	plt.title(titles[i]) # 图片标题    
	plt.xticks([]),plt.yticks([]) # 不显示横纵坐标
plt.show()

png

自适应阈值

方法cv.adaptiveThreshold还包含三个输入参数:

adaptiveMethod决定阈值是如何计算的:

cv.ADAPTIVE_THRESH_MEAN_C::阈值是邻近区域的平均值减去常数C

cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的高斯加权总和减去常数C

BLOCKSIZE确定附近区域的大小

C是从邻域像素的平均或加权总和中减去的一个常数。

# Otsu阈值法
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('noise1.png',0) # 全局阈值
ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)# Otsu阈值
ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)# 高斯滤波后再采用Otsu阈值
blur = cv.GaussianBlur(img,(5,5),0) # 使用5x5高斯核对图像进行滤波以去除噪声# 然后应用Otsu阈值处理
ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)# 绘制所有图像及其直方图
images = [img, 0, th1,img, 0, th2, blur, 0, th3]titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)','Original Noisy Image','Histogram',"Otsu's Thresholding",'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]
for i in range(3):
	plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray') # 原始图像
	plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])
	plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256) # 直方图
	plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([])
	plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray') # 阈值处理后的图像
	plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])
plt.show()

图像平滑

与一维信号一样,还可以使用各种低通滤波器(LPF),高通滤波器(HPF)等对图像进行滤波。

LPF有助于消除噪声,使图像模糊等。HPF滤波器有助于在图像中找到边缘。

函数cv.filter2D将内核与图像进行卷积

import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.imread('opencv-logo.png')#定义卷积核5X5,每个元素为1的矩阵
kernel = np.ones((5,5), np.float32)/25 # 卷积核# 将原图与内核进行卷积# 保持这个内核在一个像素上,将所有低于这个内核的25个像素相加,取其平均值,然后用新的平均值替换中心像素
dst = cv.filter2D(img,-1,kernel) # 绘制图像 
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()

#  图像会变得模糊# error: (-215:Assertion failed) dims <= 2 && step[0] > 0 in function 'cv::Mat::locateROI'# 这个error是图片的路径写错了

图像模糊(图像平滑)

通过将图像与低通滤波器内核进行卷积来实现图像模糊。这对于消除噪音很有用。它实际上从图像中消除了高频部分(例如噪声,边缘)

平均

通过将图像与归一化框滤镜进行卷积来完成的。它仅获取内核区域下所有像素的平均值,并替换中心元素。这是通过功能**cv.blur()或**cv.boxFilter()完成的。
import cv2 as cvimport numpy as npfrom matplotlib import pyplot as pltimg = cv.imread('opencv-logo.png')# 对图像进行卷积模糊
'''均值滤波cv.blur(src,ksize)
src:需要处理的图像
ksize:选取的核大小
example   cv.blur(img,(6,5))'''
blur = cv.blur(img,(5,5))
'''方框滤波cv.boxFilter(src,ddepth,ksize,normalize)
src:输入图片ddepth:目标图像的深度
ksize:核大小normalize:是否归一化,使用布尔值进行选择
example  cv.boxFilter(img, ddepth = -1, ksize(2,2),normalize = False)
'''

avg = cv.boxFilter(img, -1,(10, 10),False)
''' 高斯模糊cv.GaussianBlur() 完成,指定内核的宽度和高度,该宽度和高度应为正数和奇数。
我们还应指定X和Y方向的标准偏差,分别为sigmaX和sigmaY。如果仅指定sigmaX,则将sigmaY与sigmaX相同。如果两个都为零,则根据内核大小进行计算。
高斯模糊对于从图像中去除高斯噪声非常有效可以使用函数**cv.getGaussianKernel()** 创建高斯内核'''
gaussian = cv.GaussianBlur(img, (15, 15), 0)
'''中位模糊函数**cv.medianBlur()** 提取内核区域下所有像素的中值,并将中心元素替换为该中值。

这对于消除图像中的椒盐噪声非常有效在中值模糊中,中心元素总是被图像中的某些像素值代替。
有效降低噪音。其内核大小应为正奇数整数'''
median = cv.medianBlur(img, 5)
'''双边滤波cv.bilateralFilter() 在去除噪声的同时保持边缘清晰锐利非常有效双边滤波器在空间中也采用高斯滤波器,但是又有一个高斯滤波器,它是像素差的函数。
、空间的高斯函数确保仅考虑附近像素的模糊,而强度差的高斯函数确保仅考虑强度与中心像素相似的那些像素的模糊。'''
bilateral = cv.bilateralFilter(img,9,75,75)plt.subplot(221),plt.imshow(img),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(222),plt.imshow(blur),plt.title('Blurred')
plt.xticks([]),plt.yticks([])
plt.subplot(223),plt.imshow(avg),plt.title('BoxFilter')
plt.xticks([]),plt.yticks([])
plt.subplot(224),plt.imshow(gaussian),plt.title('GaussianBlur')
plt.xticks([]), plt.yticks([])
plt.show()

形态学转换

膨胀、腐蚀、开运算、闭运算


posted @ 2022-04-19 17:15  Funnnn  阅读(75)  评论(0编辑  收藏  举报