33、Python 图像编程:从基础操作到对象计数

Python 图像编程:从基础操作到对象计数

1. 文本标注与字体使用

在图像上进行文本标注是常见的操作。可以使用 Pillow 库的 text() 函数来实现。以下是一个简单示例:

width, height = (78, 11)
draw.text((80 - width/2, 80 - height/2), s)
img.show()

此代码展示了在考虑字符串宽度和高度的情况下进行文本标注的效果。

1.1 使用其他字体

text() 函数也支持使用其他字体。首先需要创建一个 ImageFont 对象,通过以下方式导入:

from PIL import ImageFont

导入后,可以使用 ImageFont.truetype(fontname, size) 来指定字体和大小,返回的 ImageFont 对象可作为参数传递给 text() 函数。

不过,不同系统的字体和字体名称可能不同,为了解决这个问题,可以使用 matplotlib.font_manager 模块中的 findfont() 函数。以下是使用 findfont() 函数以 Vera 字体进行文本标注的脚本:

from matplotlib import font_manager
from PIL import Image, ImageDraw, ImageFont
img = Image.new('L', (250, 100), 255)
draw = ImageDraw.Draw(img)
font_str = font_manager.findfont('Vera')
ttf = ImageFont.truetype(font_str, 54)
s = 'ABCabc'
(w, h) = draw.textsize(s, font=ttf)
draw.text(((250 - w)/2, (100 - h)/2), s, font=ttf)
img.show()

1.2 注意事项

使用字体时,在调用 text() textsize() 函数时都必须提供 font 参数。

2. 缩略图索引图像示例

之前创建的图像目录虽然有用,但无法展示图像内容。下面的示例创建了一个带有文件名标注的图像缩略图索引:

import os
from PIL import Image, ImageDraw

def thumbnail_index(dirpath):
    num_images = 5
    thumb_size = (128, 96)
    cat_size = (num_images * thumb_size[0], num_images * thumb_size[1])
    fn_index = 0
    img_index = 0
    for file in os.listdir(dirpath):
        pathname = os.path.join(dirpath, file)
        try:
            img = Image.open(pathname)
        except IOError:
            print(file, "is not an image file")
            continue
        img.thumbnail((thumb_size), Image.ANTIALIAS)
        draw = ImageDraw.Draw(img)
        draw.text((2, 2), file)
        if img_index == 0:
            thumbs_img = Image.new('RGB', cat_size)
        x = img_index % num_images
        y = img_index // num_images
        thumbs_img.paste(img, (x * thumb_size[0], y * thumb_size[1]))
        img_index += 1
        if img_index == num_images**2:
            img_index = 0
            thumbs_img.save('%s-%03d.cat.jpg' % (dirpath, fn_index))
            fn_index += 1
    if img_index:
        thumbs_img.save('%s-%03d.cat.jpg' % (dirpath, fn_index))

2.1 操作步骤

  1. 定义参数 :设置 num_images 为每行或每列的图像数量, thumb_size 为缩略图的尺寸。
  2. 遍历目录 :使用 os.listdir() 遍历指定目录下的所有文件。
  3. 打开图像 :尝试打开每个文件,如果是图像文件则创建缩略图,并在缩略图上标注文件名。
  4. 创建索引图像 :当 img_index 为 0 时,创建一个新的索引图像。
  5. 粘贴缩略图 :计算缩略图在索引图像中的位置并粘贴。
  6. 保存索引图像 :当索引图像填满时,保存图像并重置 img_index

2.2 提示

可以在图像行之间添加黑色(或白色)条纹,将文本显示在图像下方。

3. 图像的矩阵表示与颜色

图像可以用矩阵表示,每个 (x, y) 点对应矩阵的列和行,值对应颜色。颜色值取决于图像模式,例如 RGB 图像的矩阵值是一个 3 字节的元组,分别代表红、绿、蓝三种颜色。

3.1 拆分图像颜色通道

可以使用 Image 方法的 split() 函数将图像拆分为不同的颜色通道:

from PIL import Image
im = Image.open('../images/circle.png')
R, G, B, A = im.split()

3.2 获取颜色数据

使用 getdata() 函数获取颜色数据,并将其转换为 NumPy 数组:

from pylab import *
data = array(R.getdata())

3.3 转换为矩阵形式

使用 reshape() 方法将数据转换为矩阵形式:

im.size
(800, 600)
data = data.reshape(im.size)

3.4 修改图像

以下是在圆形图像中间绘制品红色条纹的示例:

from pylab import *
from PIL import Image
im = Image.open('../images/circle.png')
R, G, B, A = im.split()
data = array(R.getdata()).reshape(im.size)
(w, h) = data.shape
data[w/2 - 100:w/2 + 100, :] = 255 * ones((200, h))
R.putdata(data.reshape(h * w))
new_img = Image.merge('RGB', (R, G, B))
new_img.show()

3.5 操作步骤

  1. 读取图像 :使用 Image.open() 打开图像。
  2. 拆分通道 :使用 split() 函数将图像拆分为四个通道。
  3. 获取数据 :使用 getdata() 函数获取红色通道的数据,并转换为矩阵形式。
  4. 修改数据 :将中间 200 行的数据值设置为 255。
  5. 更新通道 :使用 putdata() 函数更新红色通道的数据。
  6. 合并通道 :使用 merge() 函数合并通道,创建新的图像。

4. 直接读取图像为 NumPy 数组

可以使用 NumPy imread() 函数直接将图像读取为 NumPy 数组,使用 imsave() 保存数组为图像,使用 matplotlib imshow() 显示图像。以下是示例:

from pylab import *
data = imread('../images/circle.png')
imshow(data)
axis('off')

4.1 修改图像

可以对 NumPy 数组进行操作来修改图像,例如反转 RGB 通道的值:

data[..., :3] = 1 - data[..., :3]
imshow(data)
imsave('../images/inverted_circle.png', data)

4.2 选择建议

如果需要使用 NumPy SciPy 的功能,建议直接将图像读取为 NumPy 数组;如果需要实际的图像操作,建议使用 Pillow

5. 图像对象计数示例(第一部分)

图像对象计数是一个复杂但有用的任务,在生物学、医学、电子制造、天文学等领域都有应用。以下是创建夜空图像并计数星星的示例。

5.1 创建星星

首先创建一个函数 star() 来创建 matplotlib 补丁对象:

from pylab import *

def star(R, x0, y0, color='w', N=5, thin=0.5):
    polystar = zeros((2 * N, 2))
    for i in range(2 * N):
        angle = i * pi / N
        r = R * (1 - thin * (i % 2))
        polystar[i] = [r * cos(angle) + x0, r * sin(angle) + y0]
    return Polygon(polystar, fc=color, ec=color)

5.2 参数说明

  • R :星星的半径。
  • x0 y0 :星星的位置。
  • color :填充和边缘颜色。
  • N :星星的尖边数量。
  • thin :星星的粗细程度(范围 0 到 1,1 表示非常细)。

5.3 列表推导式实现

也可以使用列表推导式实现 star() 函数:

def another_star(R, x0, y0, color='w', N=5, thin = 0.5):
    a = arange(0, 2 * pi, 2 * pi / N)
    r = (1 - thin) * R
    polystar = array(list(zip(R * cos(a) + x0, R * sin(a) + y0, 
        r * cos(a + pi / N) + x0, r * sin(a + pi / N) + y0)))
    return Polygon(polystar.reshape(N * 2, 2), fc=color, ec=color)

5.4 生成星星示例

以下脚本生成一些有趣的星星:

from pylab import *
from star_patch import star

examples = [
    "star(10, 0, 0, 'k')",
    "star(10, 0, 0, 'k', 10)",
    "star(10, 0, 0, 'k', 5, 0.2)",
    "star(10, 0, 0, 'k', 3, 0.9)" 
]

for i, example in enumerate(examples):
    subplot(2, 2, i + 1)
    exec("new_star=" + example)
    gca().add_patch(new_star)
    title(example)
    axis('scaled')
    axis([-10, 10, -10, 10])

show()

5.5 操作步骤

  1. 导入模块 :导入 pylab star_patch 模块。
  2. 定义示例列表 :定义包含不同星星参数的示例列表。
  3. 遍历示例 :使用 enumerate() 遍历示例列表,创建子图并执行示例代码。
  4. 添加补丁 :使用 gca().add_patch() 将星星补丁添加到子图中。
  5. 设置标题和坐标轴 :设置子图的标题和坐标轴范围。
  6. 显示图像 :使用 show() 显示图像。

总结

本文介绍了 Python 中图像编程的多个方面,包括文本标注、字体使用、缩略图索引图像创建、图像的矩阵表示与颜色处理、直接读取图像为 NumPy 数组以及图像对象计数的初步示例。通过这些示例,可以看到 Python 在图像编程方面的强大功能和灵活性。

流程图

graph TD;
    A[开始] --> B[文本标注与字体使用];
    B --> C[缩略图索引图像示例];
    C --> D[图像的矩阵表示与颜色];
    D --> E[直接读取图像为NumPy数组];
    E --> F[图像对象计数示例];
    F --> G[结束];

表格

操作 相关函数
文本标注 text()
字体使用 ImageFont.truetype() findfont()
拆分图像通道 split()
获取颜色数据 getdata()
转换为矩阵形式 reshape()
合并通道 merge()
读取图像为 NumPy 数组 imread()
保存 NumPy 数组为图像 imsave()
显示图像 imshow()
创建星星补丁 star()

6. 图像对象计数示例(后续部分)

6.1 递归与填充算法

在完成星星图像的创建后,接下来要实现计数星星的功能,这里会用到递归算法来填充图像。递归是一种函数调用自身的编程技巧,在图像填充中非常有用。

假设我们已经有了一张包含星星的夜空图像,要对星星进行计数。我们可以通过遍历图像的每个像素点,当遇到星星的像素时,使用递归算法将该星星的所有像素标记为已访问,从而实现对单个星星的填充。以下是一个简化的递归填充函数示例:

def flood_fill(image, x, y, target_color, replacement_color):
    if x < 0 or x >= image.shape[0] or y < 0 or y >= image.shape[1]:
        return
    if image[x, y] != target_color:
        return
    image[x, y] = replacement_color
    flood_fill(image, x + 1, y, target_color, replacement_color)
    flood_fill(image, x - 1, y, target_color, replacement_color)
    flood_fill(image, x, y + 1, target_color, replacement_color)
    flood_fill(image, x, y - 1, target_color, replacement_color)

这个函数接受图像、起始像素点的坐标、目标颜色和替换颜色作为参数。它会检查当前像素点是否在图像范围内且颜色是否为目标颜色,如果是则将其颜色替换为替换颜色,并递归调用自身对相邻的像素点进行同样的操作。

6.2 星星计数算法

结合上述的递归填充函数,我们可以实现星星计数算法。具体步骤如下:
1. 初始化计数器 :设置一个变量用于记录星星的数量。
2. 遍历图像 :对图像的每个像素点进行遍历。
3. 发现星星 :当遇到未访问过的星星像素时,调用递归填充函数将该星星的所有像素标记为已访问,并将计数器加 1。

以下是完整的星星计数算法示例代码:

import numpy as np

def count_stars(image):
    star_count = 0
    visited = np.zeros_like(image)
    target_color = 255  # 假设星星的颜色为白色
    replacement_color = 0  # 标记为已访问的颜色
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            if image[i, j] == target_color and visited[i, j] == 0:
                flood_fill(image, i, j, target_color, replacement_color)
                star_count += 1
    return star_count

6.3 操作步骤

  1. 导入必要的库 :导入 numpy 库用于处理图像数据。
  2. 定义递归填充函数 :实现 flood_fill 函数用于填充单个星星。
  3. 定义星星计数函数 :实现 count_stars 函数,在函数内部初始化计数器和访问标记数组,遍历图像并调用递归填充函数进行星星计数。
  4. 调用星星计数函数 :将包含星星的图像作为参数传递给 count_stars 函数,得到星星的数量。

6.4 算法扩展思路

上述的星星计数算法是一个基础的实现,在实际应用中可能需要考虑更多的因素,以下是一些扩展思路:
- 处理噪声 :图像中可能存在噪声,会影响星星的计数结果。可以在计数前对图像进行滤波处理,去除噪声。
- 不同形状和大小的星星 :当前算法假设星星是连续的白色像素区域,对于不同形状和大小的星星可能需要更复杂的处理。可以使用形态学操作(如膨胀、腐蚀)来处理星星的形状。
- 多通道图像 :如果图像是多通道的(如 RGB 图像),需要先将其转换为单通道图像,或者分别处理每个通道。

7. 总结与展望

7.1 总结

本文全面介绍了 Python 在图像编程领域的多种应用,涵盖了从基础的文本标注、字体使用,到复杂的图像对象计数等多个方面。通过使用 Pillow 库,我们可以方便地进行图像的基本操作,如文本标注、缩略图生成等;利用 NumPy matplotlib 库,能够将图像转换为矩阵形式进行数值处理,实现图像的修改和显示;而递归算法则为图像对象计数提供了有效的解决方案。

7.2 展望

随着计算机视觉和图像处理技术的不断发展,Python 在该领域的应用前景十分广阔。未来可以进一步探索以下方向:
- 深度学习应用 :结合深度学习框架(如 TensorFlow、PyTorch),实现更复杂的图像识别和分类任务,如人脸识别、物体检测等。
- 实时图像处理 :开发实时图像处理系统,应用于视频监控、自动驾驶等领域,提高系统的实时性和准确性。
- 医学图像处理 :在医学领域,利用 Python 进行医学图像的分析和诊断,如肿瘤检测、疾病预测等。

流程图

graph TD;
    A[开始] --> B[递归与填充算法];
    B --> C[星星计数算法];
    C --> D[算法扩展思路];
    D --> E[总结与展望];
    E --> F[结束];

表格

功能 相关函数 说明
递归填充 flood_fill 用于填充单个星星的像素区域
星星计数 count_stars 实现星星的计数功能
滤波处理 scipy.ndimage.filters 相关函数 去除图像噪声
形态学操作 scipy.ndimage.morphology 相关函数 处理星星的形状
根据原作 https://pan.quark.cn/s/459657bcfd45 的源码改编 Classic-ML-Methods-Algo 引言 建立这个项目,是为了梳理和总结传统机器学习(Machine Learning)方法(methods)或者算法(algo),和各位同仁相互学习交流. 现在的深度学习本质上来自于传统的神经网络模型,很大程度上是传统机器学习的延续,同时也在不少时候需要结合传统方法来实现. 任何机器学习方法基本的流程结构都是通用的;使用的评价方法也基本通用;使用的一些数学知识也是通用的. 本文在梳理传统机器学习方法算法的同时也会顺便补充这些流程,数学上的知识以供参考. 机器学习 机器学习是人工智能(Artificial Intelligence)的一个分支,也是实现人工智能最重要的手段.区别于传统的基于规则(rule-based)的算法,机器学习可以从数据中获取知识,从而实现规定的任务[Ian Goodfellow and Yoshua Bengio and Aaron Courville的Deep Learning].这些知识可以分为四种: 总结(summarization) 预测(prediction) 估计(estimation) 假想验证(hypothesis testing) 机器学习主要关心的是预测[Varian在Big Data : New Tricks for Econometrics],预测的可以是连续性的输出变量,分类,聚类或者物品之间的有趣关联. 机器学习分类 根据数据配置(setting,是否有标签,可以是连续的也可以是离散的)和任务目标,我们可以将机器学习方法分为四种: 无监督(unsupervised) 训练数据没有给定...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值