像素运算
- 算数运算
- 加、减、乘、除(两幅图像的类型、大小必须一致)
- 引用它---调节亮度
- 调整对比度
- 逻辑运算
- 或、或、非
- 应用---遮罩层控制
代码层面的知识点
- 常见的图像混合
- 算法运算和集合运算
先来看一个简单的代码:
import cv2 as cv
import numpy as np
def salt(image, n):
for k in range(n):
# random.random随机生成一个[0,1)之间的数
i = int(np.random.random() * image.shape[1])
j = int(np.random.random() * image.shape[0])
image[j, i, 0] = 255
image[j, i, 1] = 255
image[j, i, 2] = 255
return image
image = cv.imread('D:/2019-02/lena.jpg')
salt = salt(image, 500)
cv.imshow('salt', salt)
cv.waitKey(0)
cv.imwrite('D:/2019-02/lena_salt.jpg', salt)
import cv2 as cv
import numpy as np
# 加
def add_demo(m1, m2):
task = cv.add(m1, m2)
return task
# 减
def sutract_demo(m1, m2):
task = cv.subtract(m1, m2)
return task
# 除
def div_demo(m1, m2):
task = cv.divide(m1, m2)
return task
# 乘
def multi_demo(m1, m2):
task = cv.multiply(m1, m2)
return task
src1 = cv.imread('D:/2019-02/lena.jpg')
src2 = cv.imread('D:/2019-02/lena2.jpg')
task = add_demo(src1, src2)
cv.imshow('image', task)
cv.imwrite('D:/2019-02/lena_add.jpg', task)
cv.waitKey(0)
除此之外,我们还可以使用以下cv中自带的函数:
image = cv.imread('D:/2019-02/.lena.jpg')
# 均值
mean_image = cv.mean(image)
# 均值和标准差
m, dev = cv.meanStdDev(image)
# 按位取与
image = cv.bitwise_and(image)
# 按位取或
image = cv.bitwise_or(image)
# 按位取非
image = cv.bitwise_not(image)
亮度和对比度
我们还可以通过cv.addWeighted()函数来修改图像的亮度和对比度
import cv2 as cv
import numpy as np
def contrast_brightenss_demo(image, c, b):
blank = np.zeros(image.shape, image.dtype)
# 计算两个数组的加权和(dst = c*src1 + (1-c)*src2 + b)
# 如果c变大了,那么整个图像每个像素点的差异就会变大,例如,如果如果c变为二倍,那么原来像素之间 差异为4的差异就会增加到8;可用来调整对比度
# 如果b增大了,那么只是整体的像素值增加了,像素之间的差异就不会变大,所以可以调整整体的亮度
dst = cv.addWeighted(image, c, blank, 1-c, b)
cv.imshow('contrase_bri_demo', dst)
cv.imwrite('D:/2019-02/lena_con_bri.jpg', dst)
image = cv.imread('D:/2019-02/lena.jpg')
cv.imshow('image', image)
# 对比度设为1.5, 亮度每个通道上增加10
contrast_brightenss_demo(image, 1.5, 10)
cv.waitKey(0)
我们还可以使用addWeighted函数进行图像的融合
addWeighted(src1, alpha, src2, 1-alpha, 0.0 )
- 第一个参数是输入图像1
- 第二个参数表示图片1的融合比例
- 第三个参数表示输入图像2
- 第四个参数表示输入图像2的融合比例
- 第五个参数表示偏差
sr1 = cv.imread('D:/2019-02/image16.jpg')
sr2 = cv.imread('D:2019-02/image26.jpg')
cv.namedWindow('image', cv.WINDOW_AUTOSIZE)
cv.imshow('sr1', sr1)
cv.imshow('sr2', sr2)
dst = cv.addWeighted(sr1, 0.5, sr2, 0.5, 0.0)
cv.imshow('image', dst)
cv.waitKey(0)
cv.destroyAllWindows()
ROI和泛洪填充
ROI(region of interest)也就是我们感兴趣的区域
image = cv.imread('D:/2019-02/lena.jpg')
# cv.imshow('image', image)
# cv.waitKey(0)
face = image[218:364, 215:387]
# 将lena的脸转换成灰度图
gray = cv.cvtColor(face, cv.COLOR_BGR2GRAY)
# 将灰度图转换为BGR格式,但是还是灰度图,只不过通道变为3了
bgr_face = cv.cvtColor(gray, cv.COLOR_GRAY2BGR)
# 如果直接将gray赋值过来,会报错,因为图像的通道不一样
image[218:364, 215:387] = bgr_face
cv.imshow('image', image)
cv.imwrite('D:/2019-02/lena_gray_r.jpg', image)
cv.waitKey(0)
运行结果:
泛洪填充
函数定义:
cv2.floodFill(img,mask,seed,newvalue(B,G,R),(loDiff1,loDiff2,loDiff3),(upDiff1,upDiff2,upDiff3),flag)
解析:
img:需要处理的图像
mask:一般设置为长宽比img大2的通道为1的数组,其中需要处理的区域设置为0,不需要处理的区域设置为1
seed:起始像素点
newvalue:需要填充的颜色
loDiff和upDiff:需要处理的与seed颜色相近的像素点,即设seed的像素为(B0,G0,R0)。若待处理的像素的值位于(B0-loDiff,G-LoDiff,B-loDiff)到(B0+upDiff,G0+upDiff,R0+upDiff)之间的话就填充newvalue
flag:一般选用cv.FLOODFILL_FIXED_RANGE,即待处理像素与seed像素比较,若不使用这个flag,则待处理像素与相邻的已填充像素比较。
import cv2 as cv
import numpy as np
# 泛洪填充
def fill_color_demo(image):
copyImage = image.copy()
h, w = image.shape[:2]
mask = np.zeros([h+2, w+2], np.uint8)
cv.floodFill(copyImage, mask, (30, 30), (0, 0, 0), (10, 10, 10), (20, 20, 20), cv.FLOODFILL_FIXED_RANGE)
cv.imshow('fill_flood', copyImage)
cv.imwrite('D:/2019-02/lena_fillflood.jpg', copyImage)
cv.waitKey(0)
# 二值填充
def fill_binary(image1):
image = np.zeros(image1.shape, np.uint8)
image[150:200,150:200] = (255, 255, 255)
print(image[30, 30])
cv.imshow('image', image)
cv.imwrite('D:/2019-02/raw_pic.jpg', image)
cv.waitKey(0)
h, w = image.shape[:2]
mask = np.ones([h+2, w+2], np.uint8)
mask[100:300, 100:300] = 0
# 在mask为0的前提下,如果像素等于0那么就进行填充
cv.floodFill(image, mask,(100, 100), (0, 0, 255), cv.FLOODFILL_FIXED_RANGE)
# cv.floodFill(image , mask, (100, 100), (0, 0, 255), (0, 0, 0), (125, 125, 125), cv.FLOODFILL_FIXED_RANGE)
cv.imshow('image', image)
cv.imwrite('D:/2019-02/pic.jpg', image)
cv.waitKey(0)
image = cv.imread('D:/2019-02/lena.jpg')
# fill_color_demo(image)
fill_binary(image)
运行fill_color_demo函数结果:
运行fill_binary函数结果:
模糊操作
三种模糊操作的方法:
- 均值模糊
- 中值卷积
- 自定义卷积
模糊操作基本原理:
- 基于离散卷积
- 定义好每个卷积核
- 不同卷积核得到不同的卷积效果
- 模糊是卷积的一种表象
均值模糊:
均值模糊对于随机噪声有很好的去噪效果
def mean_blur(image):
# 输入一个5*5的卷积核,可以达到模糊的效果
dst = cv.blur(image,(5, 5))
cv.imshow('mean_blur', dst)
cv.imwrite('D:/2019-02/image.jpg', dst)
image = cv.imread('D:/2019-02/lena.jpg')
cv.imshow('raw_image', image)
cv.imwrite('D:/2019-02/raw_lena.jpg', image)
mean_blur(image)
cv.waitKey(0)
运行结果:
原图像:
模糊后的:
中值模糊:
中值模糊对于椒盐噪声又很好的去噪效果,见下面代码:
def median_blur(image):
dst = cv.medianBlur(image, 5)
cv.imshow('median_blur', dst)
cv.imwrite('D:/2019-02/image.jpg', dst)
image = cv.imread('D:/2019-02/lena_salt.jpg')
cv.imshow('raw_image', image)
cv.imwrite('D:/2019-02/raw_lena.jpg', image)
median_blur(image)
cv.waitKey(0)
运行结果,原图:
运行后:
可以看到,图像是变得更加模糊了,但是整个图像基本实现了去噪;对于椒盐噪声,中值模糊处理的更好
自定义模糊:
def custom_blur(image):
kernel = np.ones([5, 5], np.float32)/25
dst = cv.filter2D(image, -1, kernel)
cv.imshow('custom_image', dst)
image = cv.imread('D:/2019-02/lena_salt.jpg')
cv.imshow('raw_image', image)
cv.imwrite('D:/2019-02/raw_lena.jpg', image)
# median_blur(image)
custom_blur(image)
cv.waitKey(0)
运行结果:
边缘保留滤波(EPF)
边缘保留滤波原理????
常用的方法:
- 高斯双边
- 均值迁移
直接撸代码:
高斯双边模糊
dst = cv.bilateralFilter(src=image, d=0, sigmaColor=100, sigmaSpace=15)
其中各参数所表达的意义:
src:原图像
d:像素的邻域直径,可有sigmaColor和sigmaSpace计算可得,一般直接设置为0就可以了
sigmaColor:颜色空间的标准方差,一般尽可能大
sigmaSpace:坐标空间的标准方差(像素单位),一般尽可能小
import cv2 as cv
import numpy as np
image = cv.imread('D:/2019-02/gussian.jpg')
cv.imshow('raw_image', image)
# 高斯双边模糊
dst = cv.bilateralFilter(image, 0, 125, 10)
cv.imshow('image', dst)
cv.imwrite('D:/2019-02/gussian1.jpg', dst)
cv.waitKey(0)
结果:
模糊后:
好丑。。。。。
均值迁移滤波
dst = cv.pyrMeanShiftFiltering(src=image, sp=15, sr=20)
其中各参数所表达的意义:
src:原图像
sp:空间窗的半径(The spatial window radius)
sr:色彩窗的半径(The color window radius)
import cv2 as cv
import numpy as np
image = cv.imread('D:/2019-02/gussian.jpg')
cv.imshow('raw_image', image)
# 高斯双边模糊
# dst = cv.bilateralFilter(image, 0, 125, 10)
# 均值迁移模糊
dst = cv.pyrMeanShiftFiltering(image, sp=10, sr=50)
cv.imshow('image', dst)
cv.imwrite('D:/2019-02/gussian1.jpg', dst)
cv.waitKey(0)
运行结果:
均值迁移会导致过度模糊
直方图
在CV中调用cv2.calcHist函数绘制直方图
cv2.calcHist(image, channels, mask, histsize, range[, hist[, accumulate]]) # 返回hist
第一个参数是输入图像
第二个参数设置直方图的通道
第三个参数是mask
第四个参数是设置直方图的个数
第五个参数表示直方图中像素值的范围
最后是两个可选参数,因为将hist返回了,所以第六个参数就没什么意义了
accumulate是一个布尔值,表示直方图是否叠加
import cv2 as cv
import matplotlib.pyplot as plt
image = cv.imread('D:/2019-02/lena.jpg')
def calcDrawHist(image):
color = ['blue', 'green', 'red']
for i, color in enumerate(color):
hist = cv.calcHist([image], [i], None, [256], [0, 256])
plt.plot(hist, color=color)
plt.xlim([0, 256])
plt.show()
calcDrawHist(image)
# cv.waitKey(0)
运行结果:
上面这幅图像是我们运行lena图像得到的三通道的像素的分布图像,可以看到是很乱的,然后我们可以选择一张颜色比较单调的一幅图像
使用这张图像测试,运行结果:
可以看到这张图像大部分都是白色的,所以整个图像的平均像素值是比较高的,从这张分布图上我们也可以看出来
直方图均衡化
通过直方图提升图像的对比度,也叫直方图均衡化
image = cv.imread('D:/2019-02/image9.jpg')
# 直方图均衡化只能对灰度图进行操作
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
cv.imshow('raw_image', gray)
cv.imwrite('D:/2019-02/image9-0.jpg', gray)
# 直方图均衡化
dst = cv.equalizeHist(gray)
cv.imshow('image', dst)
cv.imwrite('D:/2019-02/image99.jpg', dst)
cv.waitKey(0)
原始图像:
灰度图:
均衡化之后的图像: