Python计算机视觉 第1章-基本的图像操作和处理

Python计算机视觉 第1章-基本的图像操作和处理

本章讲解操作和处理图像的基础知识,将通过大量示例介绍处理图像所需的Python工具包,并介绍用于读取图像、图像转换和缩放、计算导数、画图和保存结果等的基本工具。

1.1 PIL:Python图像处理类库

利用PIL中的函数,我们可以从大多数图像格式的文件中读取数据,然后写入最常见的图像格式文件中。PIL中最重要的模块为Image。要读取一幅图像,可以使用:

from PIL import Image
pil_im = Image.open('empire.jpg')

上述代码的返回值pil_im是一个PIL图像对象。

from PIL import Image
import matplotlib.pyplot as plt

# 打开并转换图像为灰度模式
pil_im = Image.open('img.png').convert('L')

# 显示图像
plt.imshow(pil_im, cmap='gray')
plt.axis('off')  # 隐藏坐标轴
plt.show()

上述代码是将图片转为灰度图像并显示出来
在这里插入图片描述

实验图1 转为灰度图象

接下来对该图生成缩略图,裁剪图片中间部分,并将图片旋转45°

from PIL import Image
import matplotlib.pyplot as plt
from matplotlib import rcParams

# 设置中文字体
rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
rcParams['axes.unicode_minus'] = False    # 解决负号显示问题

# 打开并转换图像为灰度模式
pil_im = Image.open('img.png').convert('L')

# 创建一个图形窗口,准备展示多张图片
fig, axs = plt.subplots(1, 3, figsize=(15, 5))

# 1. 显示缩略图
thumbnail_im = pil_im.copy()  # 复制图像以保持原图
thumbnail_im.thumbnail((100, 100))  # 创建缩略图,大小设置为 (100, 100)
axs[0].imshow(thumbnail_im, cmap='gray')
axs[0].set_title('缩略图')
axs[0].axis('off')

# 2. 裁剪图像的中间部分
width, height = pil_im.size
left = (width - 100) // 2
top = (height - 100) // 2
right = (width + 100) // 2
bottom = (height + 100) // 2
cropped_im = pil_im.crop((left, top, right, bottom))  # 裁剪中间 100x100 的区域
axs[1].imshow(cropped_im, cmap='gray')
axs[1].set_title('裁剪中间部分')
axs[1].axis('off')

# 3. 将图像旋转45°
rotated_im = pil_im.rotate(45)  # 旋转图像45°
axs[2].imshow(rotated_im, cmap='gray')
axs[2].set_title('旋转 45°')
axs[2].axis('off')

# 显示所有图像
plt.show()

在这里插入图片描述

实验图2 生成缩略图,裁剪图片中间部分,图片旋转45°

1.2 Matplotlib

接下来对该图像绘制图像轮廓,并生成图像的灰度直方图

from PIL import Image, ImageDraw, ImageFilter
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import rcParams

# 设置中文字体
rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体(SimHei)字体
rcParams['axes.unicode_minus'] = False    # 解决负号显示问题

# 打开图像并转换为灰度模式
img = Image.open('img.png').convert('L')

# 创建一个绘图对象
draw = ImageDraw.Draw(img)

# 绘制点
draw.point((50, 50), fill=255)  # 在 (50, 50) 位置绘制一个白色点

# 绘制线条
draw.line((20, 20, 80, 80), fill=255, width=2)  # 从 (20, 20) 到 (80, 80) 画一条白色线

# 生成图像轮廓
contour_img = img.filter(ImageFilter.CONTOUR)

# 绘制直方图
histogram = np.array(img).flatten()  # 将图像转为数组并展平
fig, axs = plt.subplots(1, 3, figsize=(18, 5))

# 原图显示
axs[0].imshow(img, cmap='gray')
axs[0].set_title('原图')
axs[0].axis('off')

# 显示轮廓图像
axs[1].imshow(contour_img, cmap='gray')
axs[1].set_title('图像轮廓')
axs[1].axis('off')

# 显示直方图
axs[2].hist(histogram, bins=256, range=(0, 255), density=True, color='gray')
axs[2].set_title('灰度直方图')

# 交互式标注功能
def onclick(event):
    if event.inaxes is not None:
        x, y = int(event.xdata), int(event.ydata)
        color = img.getpixel((x, y))
        axs[0].annotate(f'({x}, {y}) - {color}', (x, y), color='red')
        fig.canvas.draw()

fig.canvas.mpl_connect('button_press_event', onclick)

# 显示所有图像和直方图
plt.show()

在这里插入图片描述

实验图3 绘制图像轮廓,生成图像的直方图

1.3 NumPy

1.3.1 图像数组表示

以下是实验代码

import numpy as np
from PIL import Image

# 1. 从文件读取图像
img_path = 'img.png'
img = Image.open(img_path)

# 2. 将图像转换为灰度图像
gray_img = img.convert('L')
gray_img_array = np.array(gray_img)

# 3. 图像缩放
scaled_img = gray_img.resize((128, 128))
scaled_img_array = np.array(scaled_img)

# 4. 直方图均衡化
hist, bins = np.histogram(scaled_img_array.flatten(), 256, [0, 256])
cdf = hist.cumsum()  # 计算累计分布函数(CDF)
cdf_normalized = cdf * hist.max() / cdf.max()  # 归一化

# 使用 CDF 进行直方图均衡化
cdf_m = np.ma.masked_equal(cdf, 0)  # 忽略 CDF 中的 0 值
cdf_m = (cdf_m - cdf_m.min()) * 255 / (cdf_m.max() - cdf_m.min())  # 归一化 CDF
cdf = np.ma.filled(cdf_m, 0).astype('uint8')  # 填充被屏蔽的值并转换为 uint8
equalized_img_array = cdf[scaled_img_array]  # 应用均衡化后的 CDF 到图像

# 5. 确保所有图像具有相同的尺寸
gray_img_resized = gray_img.resize((128, 128))
gray_img_resized_array = np.array(gray_img_resized)

# 6. 保存图像数组到 NumPy 文件
np.savez('image_arrays.npz',
         gray_img=gray_img_resized_array,
         scaled_img=scaled_img_array,
         equalized_img=equalized_img_array)

print("图像数组已保存到 'image_arrays.npz'")

# 7. 加载保存的图像数组
data = np.load('image_arrays.npz')

# 获取所有数组的名称
array_names = data.files

# 显示每个数组的信息
for name in array_names:
    array = data[name]
    print(f"数组名称: {name}")
    print(f"形状: {array.shape}")
    print(f"数据类型: {array.dtype}")
    print(f"前几项数据: {array.flat[:10]}")  # 打印前 10 个数据项(扁平化)
    print()  # 添加空行以便于阅读

# 关闭文件
data.close()

在这里插入图片描述

实验图4 生成图像数组

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

# 1. 读取图像并转换为灰度图像
image_path = r'img.png'
original_img = Image.open(image_path).convert('L')
original_img_array = np.array(original_img)

# 2. 图像缩放
# 将图像缩放到 128x128 像素
resized_img = original_img.resize((128, 128))
resized_img_array = np.array(resized_img)

# 3. 灰度变换
# 反向处理图像
inverted_img_array = 255 - resized_img_array

# 4. 显示图像
plt.rcParams['font.sans-serif'] = ['SimHei']  # 支持中文字体

fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# 原始图像
axes[0].imshow(original_img_array, cmap='gray')
axes[0].set_title('原始图像')
axes[0].axis('off')

# 缩放后的图像
axes[1].imshow(resized_img_array, cmap='gray')
axes[1].set_title('缩放图像')
axes[1].axis('off')

# 反向处理图像
axes[2].imshow(inverted_img_array, cmap='gray')
axes[2].set_title('反向处理')
axes[2].axis('off')

plt.tight_layout()
plt.show()

在这里插入图片描述

实验图5 对图像进行反向处理和缩放

接下来对图像直方图均衡化,以下为实验代码:

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt


def histogram_equalization(image, num_bins=256):
    # 计算图像的直方图
    image_hist, bin_edges = np.histogram(image.flatten(), num_bins, density=True)

    # 计算累积分布函数 (CDF)
    cdf = image_hist.cumsum()
    cdf = 255 * cdf / cdf[-1]

    # 应用累积分布函数进行图像均衡化
    equalized_image = np.interp(image.flatten(), bin_edges[:-1], cdf)
    return equalized_image.reshape(image.shape), cdf


# 读取图像并转换为灰度图像
image_path = r'img.png'
original_image = Image.open(image_path).convert('L')
original_image_array = np.array(original_image)

# 对图像进行直方图均衡化
equalized_image_array, cdf = histogram_equalization(original_image_array)

# 显示结果
plt.rcParams['font.sans-serif'] = ['SimHei']  # 支持中文字体

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 原始图像
axes[0, 0].imshow(original_image_array, cmap='gray')
axes[0, 0].set_title('原始灰度图像')
axes[0, 0].axis('off')

# 均衡化后的图像
axes[0, 1].imshow(equalized_image_array, cmap='gray')
axes[0, 1].set_title('均衡化图像')
axes[0, 1].axis('off')

# 原始图像直方图
axes[1, 0].hist(original_image_array.flatten(), bins=128, range=(0, 256))
axes[1, 0].set_title('原始图像直方图')
axes[1, 0].set_xlabel('像素强度')
axes[1, 0].set_ylabel('频率')

# 均衡化图像直方图
axes[1, 1].hist(equalized_image_array.flatten(), bins=128, range=(0, 256))
axes[1, 1].set_title('均衡化图像直方图')
axes[1, 1].set_xlabel('像素强度')
axes[1, 1].set_ylabel('频率')

plt.tight_layout()
plt.show()

在这里插入图片描述

实验图6 对图像直方图均衡化

1.4 SciPy

SciPy是建立在NumPy 基础上,用于数值运算的开源工具包。SciPy 提供很多高效的操作,可以实现数值积分、优化、统计、信号处理,以及对我们来说最重要的图像处理功能。

1.4.1 图像模糊

以下为实验代码:

from PIL import Image
import numpy as np
from scipy.ndimage import gaussian_filter
import matplotlib.pyplot as plt

# 读取图像并转换为灰度图像
image_path = r'img.png'
original_image = Image.open(image_path).convert('L')
original_image_array = np.array(original_image)

# 应用高斯滤波
sigma = 5
smoothed_image_array = gaussian_filter(original_image_array, sigma=sigma)

# 可视化结果
plt.rcParams['font.sans-serif'] = ['SimHei']  # 支持中文字体

fig, axes = plt.subplots(1, 2, figsize=(12, 6))

# 显示原始图像
axes[0].imshow(original_image_array, cmap='gray')
axes[0].set_title('原始灰度图像')
axes[0].axis('off')

# 显示平滑后的图像
axes[1].imshow(smoothed_image_array, cmap='gray')
axes[1].set_title('高斯滤波结果')
axes[1].axis('off')

plt.tight_layout()
plt.show()

在这里插入图片描述

实验图7 图像模糊结果

1.4.2 图像导数

为了生成图像的导数,可以使用梯度算子(如 Sobel 算子)来计算图像的梯度。

以下为实验代码:

from PIL import Image
import numpy as np
from scipy.ndimage import sobel
import matplotlib.pyplot as plt

# 读取图像并转换为灰度图像
image_path = r'img.png'
original_image = Image.open(image_path).convert('L')
image_array = np.array(original_image)

# 计算图像的梯度
gradient_x = sobel(image_array, axis=0)  # x 方向的梯度
gradient_y = sobel(image_array, axis=1)  # y 方向的梯度

# 计算梯度幅度
gradient_magnitude = np.sqrt(gradient_x**2 + gradient_y**2)

# 可视化结果
plt.rcParams['font.sans-serif'] = ['SimHei']  # 支持中文字体

fig, axes = plt.subplots(1, 3, figsize=(18, 6))

# 显示原始图像
axes[0].imshow(image_array, cmap='gray')
axes[0].set_title('原始灰度图像')
axes[0].axis('off')

# 显示 x 方向梯度
axes[1].imshow(gradient_x, cmap='gray')
axes[1].set_title('X 方向梯度')
axes[1].axis('off')

# 显示 y 方向梯度
axes[2].imshow(gradient_y, cmap='gray')
axes[2].set_title('Y 方向梯度')
axes[2].axis('off')

plt.tight_layout()
plt.show()

# 可选:显示梯度幅度
plt.figure(figsize=(6, 6))
plt.imshow(gradient_magnitude, cmap='gray')
plt.title('梯度幅度')
plt.axis('off')
plt.show()

在这里插入图片描述

实验图8 生成图像导数

1.4.3 形态学:对象计数

形态学(或数学形态学)是度量和分析基本形状的图像处理方法的基本框架与集合。形态学通常用于处理二值图像,但是也能够用于灰度图像。二值图像是指图像的每个像素只能取两个值,通常是0和1。二值图像通常是,在计算物体的数目,或者度量其大小时,对一幅图像进行阈值化后的结果。

以下为示例代码:

from PIL import Image
import numpy as np
from scipy.ndimage import measurements, morphology
import matplotlib.pyplot as plt

# 读取图像并转换为灰度图像
image_path = r'img.png'
original_image = Image.open(image_path).convert('L')
image_array = np.array(original_image)

# 二值化处理
binary_image = (image_array < 128).astype(int)

# 标记二值化图像中的连通区域
labels, num_objects = measurements.label(binary_image)

# 执行开操作(形态学开操作)
structure_element = np.ones((9, 5))
opened_image = morphology.binary_opening(binary_image, structure_element, iterations=2)
opened_labels, num_opened_objects = measurements.label(opened_image)

# 可视化结果
plt.rcParams['font.sans-serif'] = ['SimHei']  # 支持中文字体

fig, axes = plt.subplots(2, 2, figsize=(12, 12))

# 显示二值化图像
axes[0, 0].imshow(binary_image, cmap='gray')
axes[0, 0].set_title('二值化图像')
axes[0, 0].axis('off')

# 显示标签图像
axes[0, 1].imshow(labels, cmap='nipy_spectral')
axes[0, 1].set_title('标签数组图像')
axes[0, 1].axis('off')

# 显示开操作后的图像
axes[1, 0].imshow(opened_image, cmap='gray')
axes[1, 0].set_title('开操作后图像')
axes[1, 0].axis('off')

# 显示开操作后的标签图像
axes[1, 1].imshow(opened_labels, cmap='nipy_spectral')
axes[1, 1].set_title('开操作后标签数组图像')
axes[1, 1].axis('off')

plt.tight_layout()
plt.show()

# 打印对象个数
print(f"开操作前对象个数为:{num_objects}")
print(f"开操作后对象个数为:{num_opened_objects}")

在这里插入图片描述

实验图9 处理结果

在这里插入图片描述

实验图10 对象计数

1.5 高级去噪

图像去噪是在去除图像噪声的同时,尽可能地保留图像细节和结构的处理技术。我们这里使用ROF(Rudin-Osher-Fatemi)去噪模型。该模型最早出现在文献[28]中。图像去噪对于很多应用来说都非常重要;这些应用范围很广,小到让你的假期照片看起来更漂亮,大到提高卫星图像的质量。ROF模型具有很好的性质:使处理后的图像更平滑,同时保持图像边缘和结构信息。

以下为实验代码,先对图像进行高斯模糊,然后再去噪:

from matplotlib import pyplot as plt
from PIL import Image
import numpy as np
from scipy.ndimage import gaussian_filter

def denoise(image, initial_guess, tolerance=0.1, tau=0.125, K=100):
    m, n = image.shape
    U = initial_guess
    px = image.copy()
    py = image.copy()
    error = 1

    while error > tolerance:
        U_old = U.copy()
        grad_x = np.roll(U, -1, axis=1) - U
        grad_y = np.roll(U, -1, axis=0) - U
        px_new = px + (tau / K) * grad_x
        py_new = py + (tau / K) * grad_y
        norm_new = np.maximum(1, np.sqrt(px_new**2 + py_new**2))

        px = px_new / norm_new
        py = py_new / norm_new

        rx_px = np.roll(px, 1, axis=1)
        ry_py = np.roll(py, 1, axis=0)
        div_p = (px - rx_px) + (py - ry_py)
        U = image + K * div_p
        error = np.linalg.norm(U - U_old) / np.sqrt(m * n)

    return U, image - U

# 读取图像并转换为灰度图像
image_path = r'img.png'
original_image = Image.open(image_path).convert('L')
image_array = np.array(original_image)

# 去噪处理
denoised_image, _ = denoise(image_array, image_array)

# 应用高斯模糊
blurred_image = gaussian_filter(image_array, sigma=10)

# 可视化结果
plt.rcParams['font.sans-serif'] = ['SimHei']  # 支持中文字体

fig, axes = plt.subplots(1, 3, figsize=(18, 6))

# 显示原始图像
axes[0].imshow(image_array, cmap='gray')
axes[0].set_title('原始图像')
axes[0].axis('off')

# 显示经过高斯模糊后的图像
axes[1].imshow(blurred_image, cmap='gray')
axes[1].set_title('高斯模糊后')
axes[1].axis('off')

# 显示去噪后的图像
axes[2].imshow(denoised_image, cmap='gray')
axes[2].set_title('去噪后')
axes[2].axis('off')

plt.tight_layout()
plt.show()


在这里插入图片描述

实验图10 处理结果

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值