OpenCV 可以进行一系列的图像处理,也能够直接的绘制图片,但涉及到一些复杂的图像处理时,没有现成的 API 可以使用,这个时候需要我们自己实现代码。
本文介绍如何利用现成的 API 去实现一个比较复杂,但可能比较常见的图像处理操作,那就时给图片添加一个透明渐变的效果。
大家可以看看效果图。
左边的图像是原始图像,右边的图像经过处理添加了一层蒙版。
需要说明的是,本文的代码基于 OpenCV3.3 和 python2.7 版本编写。
我的思路是先创立一幅透明的图像,然后在透明的图像上进行像素点颜色值的操作。
上面右边的图像就是我创建的渐变图像,它大小与原图片一样的。
我以垂直渐变为例说明。
如何实现这样的渐变呢?
我们知道 RGB 模式下,每个颜色通道的取值范围是 0 ~ 255。
我们可以给予一个起始颜色,(255,255,0)。
然后再给定一个结束颜色,(0,0,0)。
不难发现,每个通道有颜色的差距。
渐变是有范围的,范围可以用 X 和 Y 轴上的像素距离表示。
那么,建立一个公式让距离与颜色的变化产生联系,也就不难理解。
比如,所有的 y 坐标值为 0 的像素颜色都为 (255,255,0),这个起始颜色用 color_start 表示,所有的 y 坐标值为 512 的像素颜色都为 (0,0,0),用 color_end 表示渐变结束颜色。
渐变的颜色距离 dist 是 512 - 0 = 512
每个颜色通道也有颜色变化,我们分别处理就好了。
以红色通道为例。
渐变距离是 512,颜色变化幅度是 0 - 255 = -255.
我们不难得到一个颜色变化的系数 g_r
g_r = -255 / 512 = -0.498
有了这个系数,就可以通过公式计算出相对于渐变开始的位置的每个坐标颜色值。
color_dst = color_start + dist * g_r
3 个颜色通道分别处理,就可以得到完整的渐变图像。
代码如下:
def vertical_grad(src,color_start,color_end):
h = src.shape[0]
print type(src)
# 创建一幅与原图片一样大小的透明图片
grad_img = np.ndarray(src.shape,dtype=np.uint8)
# opencv 默认采用 BGR 格式而非 RGB 格式
g_b = float(color_end[0] - color_start[0]) / h
g_g = float(color_end[1] - color_start[1]) / h
g_r = float(color_end[2] - color_start[2]) / h
for i in range(h):
for j in range(src.shape[1]):
grad_img[i,j,0] = color_start[0] + i * g_b
grad_img[i,j,1] = color_start[1] + i * g_g
grad_img[i,j,2] = color_start[2] + i * g_r
return grad_img
我们可以写代码测试一下。
if __name__ == '__main__':
img = cv2.imread('../lena.jpg')
grad_img = vertical_grad(img,(0,0,255),(0,0,0))
cv2.imshow('lena',img)
cv2.imshow('gradients',grad_img)
cv2.waitKey()
cv2.destroyAllWindows()
但有了渐变图像还不够,我们需要将渐变应用到原始图像当中。
这个其实很简单,只要借助于 OpenCV 自带的混合方法就好了。
blend = cv2.addWeighted(img,1.0,test,0.6,0.0)
第一个参数是要混合的原始图片,第二个参数对应第一张图片的 alpha 值,第三个参数是要混合的图像,它与第一张图片的尺寸和通道都是一致的,后面的参数代表混合时,它的 alpha 取值。最后一位是 gamma 参数,默认为 0.
alpha 就是透明度的参数,在上面代码中,我让原始图片保持了 1.0 的透明度,而让它上面的渐变图像只有 0.6,最终实现了图像的混合操作。
测试代码:
img = cv2.imread('../lena.jpg')
grad_img = vertical_grad(img,(0,255,255),(0,0,0))
blend = cv2.addWeighted(img,1.0,grad_img,0.6,0.0)
cv2.imshow('lena',img)
cv2.imshow('gradients',grad_img)
cv2.imshow('blend',blend)
q = cv2.waitKey()
cv2.destroyAllWindows()
本文只讲了一个方向的渐变效果,其他方向大家可以自己思考一下,想想怎么实现,其实思路差不多。