import code
#import craft
#import design
#import others
difficulty = "easy-medium"
今天为大家带来的是前景与背景叠加操作的Python代码实现。
和画图软件同属于平面图制作软件的PhotoShop,为我们带来的最突出的变革,莫过于引入了图层的概念。有了图层,我们可以在背景上提前绘制好前景,而不用担心位置不对,或者前景覆盖掉背景之类的问题。
前景图就像代码中的函数块一样,可以提前定义好,以后想搁哪用搁哪用。
一张前景图和一张完整的矩形图片不同,它存在很多透明或是半透明的像素点。因此,传统图片的RGB三通道表示法就不够用了,需要引入第四个通道——Alpha,来表示每一个像素点的透明度。也就是说,一张带Alpha通道的图片所包含的信息,需要用一个width*height*4的三维向量来存储。
在OpenCV中,我们照常可以使用cv.imread方法打开一张Alpha图片(通常为png格式),不过该方法默认会将图片以三通道的形式导入,自动忽略掉Alpha信息。所以需要在图片地址后面添加一个参数(cv.IMREAD_UNCHANGED)来保证导入的图片为四通道图片。
import cv2 as cv
import numpy as np
foreground = cv.imread("hand.png", cv.IMREAD_UNCHANGED)
background = cv.imread("bg.jpg")
在导入了前景和背景图之后,我们需要考虑如何把他们叠加在一起。假设你的前景和背景拥有相同的长和宽。现在,用OpenCV自带的cv.add方法试试看?
test = cv.add(foreground, background)
------------------------------------
error Traceback (most recent call last)
-2-037009c9cb31> in 5 background = cv.imread("bg.jpg")6
----> 7 test = cv.add(foreground, background)
error: OpenCV(3.4.2) /opt/concourse/worker/vol
umes/live/0851a95b-0a19-4f3a-61a7-502b925fea13
/volume/opencv-suite_1535642710601/work/module
s/core/src/arithm.cpp:659: error: (-209:Sizes
of input arguments do not match) The operationis neither 'array op array' (where arrays hav
e the same size and the same number of channel
s), nor 'array op scalar', nor 'scalar op arra
y' in function 'arithm_op'
显然这样不行。弹出的错误提示告诉我们,用来加和的向量必须有相同的 size 和 channel 才行。
那我们用 cv.split 和 cv.merge 方法把Alpha通道去掉试试看?
b,g,r,alpha = cv.split(foreground)
foreground_BGR = cv.merge((b,g,r))
test = cv.add(foreground_BGR, background)
cv.imshow("img", test)
cv.waitKey(0)
cv.destroyAllWindows()
虽然不报错了,可还是不行。
cv.add 方法的实现只是简单的像素点数值相加和,加起来大于255的像素点的值会变成255。因此,这个操作会把大部分区域的色值加到一个很高的值。
尤其是,PS在生成透明图的时候,会自动把Alpha=0的像素点的rgb值定为255,也就是白色。所以求和之后,透明区域只能得到白色。除非你很喜欢这个颜色,否则杰尼龟不建议你对喜爱的图片进行这个操作哟。
这时候,被我们冷落许久的Alpha通道看不下去了,大声喊道:我才是今天的主角!
别急,我们这就来临幸你。
首先,我们拆开前景的四个通道,将b, g, r三个通道merge成一张图片。然后把Alpha通道复制三份,同样merge成一张图片。
b,g,r,alpha = cv.split(foreground)
foreground_BGR = cv.merge((b,g,r))
alpha = cv.merge((alpha,alpha,alpha))
然后,我们将alpha中的每一个数除以255,得到一个被压扁的Alpha通道。
alpha = alpha.astype(float)/255
注意,此时Alpha向量的数据类型已经从 np.uint8 型自动变为 float 型。为了用cv实现四则运算,我们需要把前景、背景图的数据类型也改为 float 型。
foreground_BGR = foreground_BGR.astype(float)
background = background.astype(float)
接下来就是Alpha图层的高光时刻了。
它已经不再是普通的Alpha通道了,我们不妨给它起一个更厉害的名字,Alpha蒙版。我们拿它直接和去掉透明通道的前景图相乘,啪,我们就得到了一张黑底图!
foreground_BGR = cv.multiply(alpha, foreground_BGR)
然后,再用 1.0 减去这个Alpha蒙版,我们就得到了它的反蒙版。用它和背景图相乘,啪,我们就得到了一张抠去手势的背景图!
background = cv.multiply(1.0 - alpha, background)
把这两张图用 cv.add 加在一起...(记得把数据类型改回 np.uint8 )
outImage = cv.add(foreground_BGR, background).astype(np.uint8)
砰!叠加完成!前景图和背景图完美地融合在了一起。
嗯,一个看似简单的操作,实际上后台做的事情挺多的,挺喧闹的。
想象一个上百个图层的PSD文件,当你点击另存为,选择jpg格式,点击保存的那一刻,你可能会听到后台隐隐约约传来的...
“突突突突突突突突突突突...”
—————————— End ——————————
『 机枪扫射声中我们寻找遮蔽的战壕
儿时沙雕的城堡毁坏了重新盖就好 』
《最后的战役》——周杰伦
周更太难了…
这次的背景图是MacBook Pro 15年版的拆机实拍图。前两天,杰尼龟刚刚通过非官方渠道,为服役4年的MBP换了块电池,顺便除了下多年的积灰。清完灰,MBP的主板布阵一下子映入眼帘。六块不同大小的电池,沿着风扇的弧边排列的电子元件,集成感爆棚。所以杰尼龟立马把它拍了下来,设成了电脑桌面。
想要原图的同学们可以向后台回复 insideMac() 索取(记得区分大小写哦)。
想要原代码的同学可回复 alpha() 获取Github链接。
作为OpenCV的初学者,我的代码从实现的角度肯定有很多不成熟的地方,希望CV大佬或者Numpy大佬不吝指点,我会感激不尽。
致匠心。