图像处理全解析:从星空模拟到算法优化
1. 星空模拟图像的生成
首先,我们来看如何创建一个模拟的夜空图像。可以对
star()
补丁对象进行额外的工作,例如添加旋转参数,通过改变
star()
函数中
sin()
和
cos()
函数的参数,将整个星星旋转指定度数。另外,还可以实现空心星星,通过分离
edgecolor
和
facecolor
功能来实现。
以下是生成夜空图像的代码:
# create a fictitious night sky
from pylab import *
from random import randrange as rr
from star_patch import star
# parameters for the simulated night sky image
img_size = 800
num_stars = 25
# star parameters: number of pointy edges and radius
min_num_points = 5
max_num_points = 11
min_star_radius = 2
max_star_radius = 10
# star parameter 'thinness' is on a scale of 1 to 10
min_thin = 5
max_thin = 9
# draw the night sky
figure(facecolor='k')
cur_axes = gca()
# patch stars
for i in range(num_stars):
new_star = star(rr(min_star_radius, max_star_radius),
rr(0, img_size) , rr(0, img_size), 'w', \
rr(min_num_points, max_num_points), \
rr(min_thin, max_thin)/10.0)
cur_axes.add_patch(new_star)
# modify axis behaviour
axis([0, img_size, 0, img_size])
axis('scaled')
axis('off')
# save the figure without the extra margins
savefig('../images/nightsky', facecolor='k', edgecolor='k')
在这段代码中,我们定义了模拟夜空图像的参数,包括图像大小、星星数量、星星的点数、半径和“瘦度”等。然后通过循环创建随机形状和位置的星星,并将它们添加到当前图形中。最后,调整坐标轴的行为并保存图像。
2. 填充算法与递归
接下来,我们介绍递归和填充算法。递归是指函数调用自身的情况,常见的递归算法有阶乘运算和斐波那契数列。我们将使用递归实现填充算法,即洪水填充(Flood Fill)。
洪水填充是一种将特定颜色的封闭区域填充为另一种颜色的算法,在大多数图像处理应用中很常见。我们假设要填充的图像是一个 NumPy 二维数组(矩阵),这样做有两个原因:
- 更具通用性:只要能将对象转换为 NumPy 数组,就可以将洪水填充算法应用到其他对象上。
- 代码更易查看:通过索引矩阵元素比使用
Image
对象的
getpixel()
和
setpixel()
方法更方便。
以下是洪水填充算法的实现代码:
from numpy import *
from sys import getrecursionlimit
def flood_fill(x, y, m, total):
"""A function to flood fill an image (matrix)."""
if total > getrecursionlimit():
return total
# nothing to fill
if m[x, y] != 1.0:
return total
m[x, y] = 0.5
if(x-1 >=0):
total = flood_fill(x-1, y, m, total+1)
if(x+1 <= m.shape[0]-1):
total = flood_fill(x+1, y, m, total+1)
if(y-1 >=0):
total = flood_fill(x, y-1, m, total+1)
if(y+1 <= m.shape[1]-1):
total = flood_fill(x, y+1, m, total+1)
return total+1
该函数接受填充点的坐标
(x, y)
、NumPy 矩阵
m
和用于跟踪递归深度的变量
total
。如果递归深度超过 Python 的限制,函数将返回当前的
total
值。如果当前点的值不是 1.0,则不进行填充。否则,将该点的值改为 0.5,并递归调用自身填充相邻的点。
洪水填充算法还可以用于扫雷游戏的编码。当用户点击一个方块时,该算法可以决定揭示多少个方块。
3. 图像中对象的计数
实现了洪水填充算法后,我们可以很容易地对图像中的对象进行计数。基本思路是遍历图像中的每个点,调用
flood_fill()
函数进行填充。如果返回值大于 0,则表示填充了一个对象,计数器加 1。
以下是计数对象的代码:
from pylab import *
from PIL import Image
from sys import getrecursionlimit
from flood_fill import flood_fill
# read the image, and retrieve the Red band
data = imread('../images/nightsky.png')[..., 0]
rows, cols = data.shape
# set all values that are nonzero to 1.0
# (could be values due to antialiasing)
data[nonzero(data)] = 1.0
# count the stars
count, recursion_limit_reached = 0, 0
for i in range(rows):
for j in range(cols):
tot = flood_fill(i, j, data, 0)
if tot > getrecursionlimit():
recursion_limit_reached += 1
elif tot > 0:
count+=1
if recursion_limit_reached:
print("Recursion limit reached %d times" % recursion_limit_reached)
print("I counted %d stars!" % count)
imshow(data)
在这段代码中,我们首先读取夜空图像的红色通道,并将非零值设置为 1.0。然后遍历图像的每个像素,调用
flood_fill()
函数进行填充。如果返回值超过递归限制,记录递归限制达到的次数;如果返回值大于 0,增加星星的计数。最后,输出递归限制达到的次数和星星的计数,并显示处理后的图像。
4. 算法的优化
在实际应用中,我们可能会遇到一些问题,例如算法返回的星星数量与实际不符。可能的原因是星星重叠,导致算法将多个对象合并为一个。另外,图像中的噪声也可能导致算法计数过多。
为了解决这些问题,我们可以采取以下优化措施:
- 只计数大小大于固定值的对象:在读取
flood_fill()
函数的返回值时,丢弃大小过小的对象。
- 图像预处理:使用滤波器对图像进行预处理,例如阈值处理和中值滤波。
此外,我们还可以通过读取
flood_fill()
函数的返回值来找到最大的对象,或者通过计算所有星星的面积来评估夜空的亮度。对于一些小对象,我们可以考虑将对角线单元格视为相邻单元格,修改洪水填充算法。
5. 图像算术运算
Pillow 提供了一组通过
ImageChops
模块实现的算术运算。例如,我们可以使用
ImageChops.invert()
函数将图像的背景颜色反转,这样在打印图像时可以节省墨水。
以下是反转图像的代码:
from PIL import Image, ImageChops
im = Image.open('../images/nightsky.png')
new_img = Image.new('RGB', (im.size[0]*2, im.size[1]))
new_img.paste(im, (0, 0))
new_img.paste(ImageChops.invert(im), (im.size[0], 0))
new_img.show()
在这段代码中,我们打开夜空图像,创建一个新的图像,将原始图像和反转后的图像粘贴到新图像中,并显示新图像。
ImageChops
模块还提供了其他一些算术运算,如下表所示:
| 函数 | 描述 |
| — | — |
|
add(img1, img2, scale=1.0, offset=0)
| 将两个图像相加:
(img1+img2)/scale+offset
,默认值表示简单相加。 |
|
constant(img1, value)
| 返回一个大小与
img1
相同、填充指定颜色值的图像。 |
|
darker(img1, img2)
| 返回一个包含两个图像中较暗像素的图像,逐像素取最小值。 |
|
difference(img1, img2)
| 返回两个图像的绝对差值,逐像素计算
abs(img1-img2)
。 |
|
lighter(img1, img2)
| 返回一个包含两个图像中较亮像素的图像,逐像素取最大值。 |
|
subtract(img1, img2, scale=1.0, offset=0)
| 将两个图像相减:
(img1-img2)/scale+offset
,默认值表示简单相减。 |
我们可以使用这些简单的运算创建一些有趣的效果,并将其用于快速图像处理算法。例如,使用
lighter()
方法处理两个夜空图像:
from PIL import Image, ImageDraw, ImageFont, ImageChops
from matplotlib import font_manager
# read the images
img1 = Image.open('../images/nightsky1.png')
img2 = Image.open('../images/nightsky2.png')
# create a new image, made of the lighter of the two
img3 = ImageChops.lighter(img1, img2)
# create a collage of three images
width, height = img1.size
delta = 10
img = Image.new('RGB', (width*2+delta, height*2+delta), (255, 255, 255))
img.paste(img1, (0, 0))
img.paste(img2, (width+delta, 0))
img.paste(img3, ((width+delta)//2, height+delta))
# annotate the images with text
font_str = font_manager.findfont('Vera')
ttf = ImageFont.truetype(font_str, 54)
draw = ImageDraw.Draw(img)
draw.text((delta, delta), 'Night Sky (1)', fill='white', font=ttf)
draw.text((delta*2+width, delta), 'Night Sky (2)', fill='white', font=ttf)
draw.text(((width+delta)//2+delta, height+delta*2), \
'Combined', fill='white', font=ttf)
# display the final image
img.show()
在这段代码中,我们读取两个夜空图像,使用
lighter()
方法创建一个新的图像,然后将三个图像组合成一个拼贴画,并添加文字注释,最后显示最终图像。
6. 图像滤波
大多数基于 GUI 的图像处理应用都提供了一系列图像滤波器。常见的滤波类别包括模糊、增强、边缘检测等。
从图像处理的角度来看,图像滤波器是帮助我们实现特定效果的已知操作。例如,在计数印刷电路板浸泡在水中的气泡数量时,我们需要使用滤波器对图像进行预处理,以去除噪声。
Pillow 提供了
ImageFilter
类,支持多种滤波器。以下是使用
MinFilter
滤波器的示例代码:
from PIL import Image, ImageChops, ImageFilter
img = Image.open('../images/nightsky.png')
inv_img = ImageChops.invert(img)
filt_img = inv_img.filter(ImageFilter.MinFilter(15))
filt_img.show()
在这段代码中,我们打开夜空图像,反转图像的背景颜色,然后使用
MinFilter
滤波器对图像进行处理,并显示滤波后的图像。
ImageFilter
还提供了一些固定的图像增强滤波器,通过大写名称可以很容易地区分它们:
from PIL import ImageFilter
[filt for filt in dir(ImageFilter) if filt.isupper()]
输出结果为:
['BLUR', 'CONTOUR', 'DETAIL', 'EDGE_ENHANCE', 'EDGE_ENHANCE_MORE', 'EMBOSS',
'FIND_EDGES', 'SHARPEN', 'SMOOTH', 'SMOOTH_MORE']
综上所述,我们介绍了从星空模拟图像的生成到图像滤波的一系列图像处理技术。通过这些技术,我们可以实现各种有趣的图像处理效果,并解决实际应用中的问题。希望这些内容对你有所帮助!
图像处理全解析:从星空模拟到算法优化
7. 操作步骤总结
为了更清晰地展示整个图像处理过程,我们将上述各个部分的操作步骤进行总结:
1.
星空模拟图像生成
- 定义模拟夜空图像的参数,如图像大小、星星数量、星星的点数、半径和“瘦度”等。
- 导入
randrange()
函数并简化命名。
- 在
for
循环中创建随机形状和位置的星星,并添加到当前图形。
- 更新图像大小,移除坐标轴。
- 保存图像到文件。
2.
洪水填充算法实现
- 假设图像为 NumPy 二维数组。
- 编写
flood_fill()
函数,接受填充点坐标、NumPy 矩阵和递归深度变量。
- 在函数中处理递归深度限制和填充条件,递归调用自身填充相邻点。
3.
图像中对象计数
- 读取夜空图像的红色通道,并将非零值设置为 1.0。
- 遍历图像的每个像素,调用
flood_fill()
函数进行填充。
- 根据返回值记录递归限制达到的次数和星星的计数。
- 输出结果并显示处理后的图像。
4.
算法优化
- 只计数大小大于固定值的对象。
- 使用滤波器对图像进行预处理。
- 读取
flood_fill()
函数返回值找到最大对象或评估夜空亮度。
- 考虑将对角线单元格视为相邻单元格修改洪水填充算法。
5.
图像算术运算
- 使用
ImageChops.invert()
函数反转图像背景颜色。
- 利用
ImageChops
模块的其他算术运算创建有趣效果。
6.
图像滤波
- 导入
ImageFilter
类。
- 打开图像,可选择反转背景颜色。
- 使用
ImageFilter
类的滤波器对图像进行处理。
- 显示滤波后的图像。
8. 流程图展示
下面是整个图像处理过程的 mermaid 流程图:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([开始]):::startend --> B(星空模拟图像生成):::process
B --> C(洪水填充算法实现):::process
C --> D(图像中对象计数):::process
D --> E{是否需要优化?}:::decision
E -- 是 --> F(算法优化):::process
E -- 否 --> G(图像算术运算):::process
F --> G
G --> H(图像滤波):::process
H --> I([结束]):::startend
9. 注意事项
在使用上述技术进行图像处理时,需要注意以下几点:
-
递归深度限制
:Python 对递归深度有限制,使用
flood_fill()
函数时可能会超过该限制。可以通过
sys.setrecursionlimit()
增加限制,但可能会导致内存问题。因此,最好在代码中检测递归深度并及时通知用户。
-
图像质量
:实际图像可能存在噪声和不完美的情况,在使用算法前需要进行预处理,如滤波和阈值处理。
-
滤波器选择
:不同的滤波器适用于不同的场景,需要根据具体需求选择合适的滤波器。例如,
MinFilter
适用于去除图像中的小噪声,而
BLUR
滤波器适用于模糊图像。
10. 总结与展望
通过本文的介绍,我们了解了从星空模拟图像生成到图像滤波的一系列图像处理技术。这些技术包括递归、洪水填充算法、对象计数、算法优化、图像算术运算和图像滤波等。通过合理运用这些技术,我们可以实现各种有趣的图像处理效果,并解决实际应用中的问题。
在未来的图像处理中,我们可以进一步探索以下方向:
-
更复杂的算法优化
:针对不同的图像场景,开发更高效、更准确的算法优化策略。
-
深度学习应用
:结合深度学习技术,实现更智能的图像处理,如自动识别图像中的对象。
-
实时图像处理
:开发实时图像处理系统,满足一些对处理速度要求较高的应用场景。
希望本文能够为你在图像处理领域的学习和实践提供帮助,让你能够更好地掌握和运用这些技术。
总之,图像处理是一个充满挑战和机遇的领域,不断探索和创新将为我们带来更多的惊喜和成果。让我们一起在这个领域中不断前行,创造出更美好的图像世界!
超级会员免费看
10万+

被折叠的 条评论
为什么被折叠?



