Image模块应用实例之彩色图像转黑白图像
说在前面
PIL库的Image模块是浙教版《信息技术必修一数据与计算》第三章的重要内容,教材中好几个“实践与体验”案例都涉及到Image模块,答题卡填涂识别项目更是Image模块的经典应用实例。
我在备课过程中发现这些案例都非常精彩,但由于课时的限制,不可能全部在课堂上呈现,于是打算把他们整理出来,作为拓展阅读资料,让学生在课后自主学习。
“Image模块应用实例分析”是一个专题系列,展示了我在备课过程中的一些思考和教学思路,有不成熟之处,敬请各位老师批评指正。
图像处理库PIL有九种不同模式:1、L、P、RGB、RGBA、CMYK、YCbCr、I、F。其特征描述如下:
(二)使用Image内置方法转换图像模式
我们今天主要学习RGB彩色图像、灰度图像和黑白图像。
首先我们来查看Image对象的基本属性,例如已知有彩色图像boy_RGB.jpg和灰度图像boy_L.jpg,运行下列代码:
#【示例程序1】
from PIL import Image
img = Image.open('boy_RGB.jpg')
print(img.format, img.size, img.mode)
img2 = Image.open('boy_L.jpg')
print(img2.format, img2.size, img2.mode)
则程序输出结果为:
JPEG (357, 287) RGB
JPEG (357, 287) L
分别输出了图像的格式、大小(宽、高)和模式。
然后我们使用convert()方法来获取不同模式的图像,例如下列代码能够将彩色图像boy_RGB.jpg转换成灰度图像boy_L.jpg:
#【示例程序2】
from PIL import Image
img = Image.open('boy_RGB.jpg')
img2 = img.convert("L") # 转换成“L”模式灰度图像
img2.save("boy_L.jpg")
当convert()方法的参数mode=”1”时,可以将"L"或“RGB”图像转换为二值黑白图像,其参数dither默认值为None(改成1也有相同效果),表示使用弗洛伊德-斯坦伯格抖动来近似原始图像的亮度级别。如果dither=0,表示抖动为无,则所有大于128的值设置为255(白色) ,所有其他值设置为0(黑色)。效果图如图3和图4所示。
下面的代码可以实现将彩色图像boy_RGB.jpg转换成黑白图像boy_10.jpg(无抖动效果)和boy_11.jpg(有抖动效果)功能:
#【示例程序3】
from PIL import Image
img = Image.open('boy_RGB.jpg')
img2 = img.convert("1", dither=0) #参数dither=0表示无抖动效果
img2.save("boy_10.jpg")
img3 = img.convert("1", dither=1) #参数dither=1表示有抖动效果
img3.save("boy_11.jpg")
(三)使用自定义函数将彩色图像转换成黑白图像
除了直接使用convert()方法将彩色图像转换成黑白图像,我们也可以利用图像模式转换原理,设置自定义函rgb_bw()来实现转换功能。
首先根据由“RGB”转换为“L”模式的公式:L = R*0.299 + G*0. 587 + B*0.114,计算出每个像素点的灰度值gray,再判断gray与阈值的关系,若gray小于阈值,设置为黑色,否则设置为白色。阈值通常取值为128,也可以根据需要设置不同的阈值。
#【示例程序4】
from PIL import Image
# 将“RGB”彩色图像转换为二值黑白图像
def rgb_bw(img):
for i in range(img.width):
for j in range(img.height):
R, G, B = img.getpixel((i, j))
# 计算像素点颜色的灰度值
gray = 0.299 * R + 0.587 * G + 0.114 * B
if gray < 128: # 小于阈值,设置为黑色
img.putpixel((i, j), (0, 0, 0))
else:
img.putpixel((i, j), (255, 255, 255))
return img
img = Image.open('boy_RGB.jpg')
img2 = rgb_bw(img)
img2.save('boy_bw_128.jpg')
设置不同的阈值,获得不同黑白图像的效果图如图5、6、7所示。
(四)更高效的修改像素点元素值方法
示例程序4中使用img.getpixel((i, j))方法来获取像素点(i, j)的RGB颜色值,其结果返回一个包含RGB三原色的元组,例如红色(255,0,0);使用img.putpixel((i, j), (0, 0, 0))方法来为像素点(i, j)设置颜色,(0, 0, 0)表示黑色,你也可以设置其他的不同颜色。
这个两个方法的名称都很长,不便于记忆,效率也不高。更常见的做法是先使用pix = img.load()方法获取整个图像所有的像素点颜色,再利用pix[i, j]来表示像素点(i, j)的颜色值,这样既可以读取其颜色值,也可以设置其颜色值,方便且高效。参考代码如下:
from PIL import Image
def rgb_bw(img):
pix = img.load()
for i in range(img.width):
for j in range(img.height):
R, G, B = pix[i, j]
gray = 0.299 * R + 0.587 * G + 0.114 * B
if gray < 128:
pix[i, j] = (0, 0, 0)
else:
pix[i, j] = (255, 255, 255)
return img
img = Image.open('boy_RGB.jpg')
img2 = rgb_bw(img)
img2.save('boy_bw_128.jpg')
(五)使用matplotlib模块生成黑白图像
教材中使用numpy和matplotlib模块来处理图像,也可以达到相同的效果,参考代码如下:
#【示例程序6】
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# 打开图像并转换成数字矩阵
img = Image.open('boy_RGB.jpg')
img.show()
img = np.array(img.convert('L'))
# 调整每个像素的RGB值
rows, cols = img.shape # 图像尺寸分别赋值
for i in range(rows): # 依次取每个像素的坐标
for j in range(cols):
if img[i, j] < 128: # 小于阈值,设置为黑色
img[i, j] = 0
else:
img[i, j] = 1
# 生成新的图像
plt.figure('lena') # 指定当前绘图对象
plt.imshow(img, cmap='gray') # 显示灰度图像
plt.axis('off') # 关闭图像坐标
plt.savefig('boy_bw_2.jpg') # 保存图片
plt.show() # 弹出图片窗口
教材提供的代码导入了numpy模块,并将图像转换成数字矩阵,然后通过二重循环遍历所有像素点,实现了将灰度图像转换成黑白图像的功能。这种算法虽然简单易懂,但是没有充分发挥numpy模块快速处理数组的优势,效率不高。
如果你熟悉numpy模块,就没有必要编写二重循环来修改像素点的颜色值了,完全可以调用numpy模块的where()方法,直接一行语句搞定。
你知道该怎么做吗?
需要本文word文档、源代码和拓展思考答案的,可以加入“Python算法之旅”知识星球参与讨论和下载文件,“Python算法之旅”知识星球汇集了数量众多的同好,更多有趣的话题在这里讨论,更多有用的资料在这里分享。
我们专注Python算法,感兴趣就一起来!
相关优秀文章: