关于Numpy处理图像和提取图像色位,我首先是参考了如下的b站视频。
一个10分钟的numpy入门教程(bilibili)
视频中说图像是RGB三个色位,但是我按照Mac微信截图得到的png图像却是有4个色位。经探究,前三个色位分别代表红R、绿G、蓝B。第四个色位代表透明度,从0(完全透明)到255(完全不透明)。
如果缺少第四个色位,那么默认会将透明度设为255。
导入图像并将其转化为ndarray矩阵对象的代码为
from PIL import Image
import numpy as np
impho = Image.open('/Users/mac/Documents/photo.png')
im = np.array(impho)
print(type(impho))
print(type(im))
print(im.shape)
输出为
(670, 1004, 4)
这说明impho
为PngImageFile
型的对象,而im
为ndarray
型的对象。
此外,im
有3个维度,其中第3个维度有4个色位。
通过代码
Image.fromarray(im).show()
展示出原始图片如下,这是某大学食堂的照片。
因为第四个色位,即透明度是我们基本不需要用的,所以通过
im = np.delete(im, -1, axis=2)
将第四个色位删除。
按道理,这时采用
im_r = im[:, :, [0, 1]]
Image.fromarray(im_r).show()
即可绘制出仅含红R、绿G的图像,但是绘制的结果为
并不是彩色的,而是黑白的图像。
我认为Python在绘制PngImageFile
类型的图像时,如果色位小于2,他便不会绘制出彩色的图像。
为了得到彩色的仅含红R、绿G的图像,我们进行如下处理
im = np.insert(im, 3, values=0, axis=2)
首先,这一句的作用是增加了第四个色位,值全为0。但是我们不将其理解为透明度。虽然放在第四个色位就是透明度,但是这里我们仅仅是借用其作为一个工具。
接下来再运用代码
im_r = im[:, :, [0, 1, 3]]
Image.fromarray(im_r).show()
绘制出的图像为:
这就完美的提取出了红R、绿G的图像。在这里,我们仅仅把值全为0的第四个色位替代了第三个色位,即蓝B色位。这样PngImageFile
类型的对象色位数已经达到了3,于是便能够绘制出彩色图像。
上述绘图处理完整的代码为
from PIL import Image
import numpy as np
impho = Image.open('/Users/mac/Documents/photo.png')
im = np.array(impho)
im = np.delete(im, -1, axis=2)
im = np.insert(im, 3, values=255, axis=2)
im_r = im[:, :, [0, 1, 3]]
Image.fromarray(im_r).show()
类似的,我们可以绘制红R、绿G、蓝B中任一个,或任两个组合的彩色图像,只需要改变定义im_r
的那一句。
注意,在定义im_r
的那一句当中,三个色位的顺序是值得小心的。
红绿组合:
im_r = im[:, :, [0, 1, 3]]
红蓝组合:
im_r = im[:, :, [0, 3, 2]]
绿蓝组合:
im_r = im[:, :, [3, 1, 2]]
红色图像:
im_r = im[:, :, [0, 3, 3]]
绿色图像:
im_r = im[:, :, [3, 1, 3]]
蓝色图像:
im_r = im[:, :, [3, 3, 2]]
对上述代码进行了推广,希望对于任意权重的红R、绿G、蓝B,都能够绘制出加工后的图像,于是给出了下述代码。
from PIL import Image
import numpy as np
impho = Image.open('/Users/mac/Documents/photo.png')
im = np.array(impho)
im = np.delete(im, -1, axis=2)
weight1 = input("请输入红R的权重:")
weight2 = input("请输入绿G的权重:")
weight3 = input("请输入蓝B的权重:")
weight1 = np.array(weight1,dtype="float64")
weight2 = np.array(weight2,dtype="float64")
weight3 = np.array(weight3,dtype="float64")
im1 = im[:, :, 0].astype("float64")
im2 = im[:, :, 1].astype("float64")
im3 = im[:, :, 2].astype("float64")
Red = np.around(weight1*im1).astype("uint8")
Green = np.around(weight2*im2).astype("uint8")
Blue = np.around(weight2*im2).astype("uint8")
im = np.insert(im, 3, values=Red, axis=2)
im = np.insert(im, 4, values=Green, axis=2)
im = np.insert(im, 5, values=Blue, axis=2)
im_r = im[:, :, [3,4,5]]
Image.fromarray(im_r).show()
这个代码修修改改了很多遍。首先,input
得到的数据类型dtype
为"
"uint8"
。这就需要进行转化,否则无法进行相乘操作。但是"uint8"
类型必然是整数,而input
类型的数只能是0至1之间的数,不能直接将其数据类型转化为"uint8"
,否则失去了权重的意义。
而基于位图的数据类型为"uint8"
的整型,所以无可奈何这里的权重也要进行取整处理,这是有误差的。
运行上述代码,我们以
请输入红R的权重:0.8
请输入绿G的权重:0.9
请输入蓝B的权重:0.2
为例,得出的图像为
有一点复古怀旧滤镜的感觉。
看起来,以后在给照片加滤镜的时候,也可以借助Python完成。
2022年10月3日于北京