Python图像批量缩放实战:从原理到命令行工具构建

Alice
Alice
发布时间:2026-10-14| 6人浏览过
原创

本文深入解析Python中图像缩放的底层原理,对比OpenCV与Pillow两大库的实现差异,并指导读者使用Click库构建一个支持批量处理、参数配置的命令行图像缩放工具。

作者:Nicholas Ballard 编译:VK 来源:Towards Data Science

图像缩放的需求与背景

可以说,每一位使用计算机的用户都曾在某个时刻需要调整图像尺寸。无论是macOS自带的预览应用,还是Windows PowerToys,都提供了这一功能。

本文将利用Python语言来实现图像缩放,幸运的是,图像处理与命令行操作正是Python的两大优势领域。

本文旨在向你展示以下三点内容:

  1. 图像的基本概念。
  2. 用于操作图像的Python库。
  3. 你可以直接在自己的项目中复用本文提供的代码。

我们将构建一个命令行程序,它能够一次性调整一个或多个图像文件的尺寸。

理解图像数据结构与纯Python实现

创建图像

在这个示例中,我们将自行创建图像,而不是寻找现成的图片进行操作。

原因在于,创建图像是理解图像本质的绝佳方式。这种缩放程序同样适用于Instagram等平台。

那么,图像究竟是什么?在Python的数据术语中,图像被定义为整数元组的列表。


image = list[list[tuple[*int, float]]]

NumPy将其定义为一个二维形状数组 (h, w, 4),其中h代表高度像素数(上下方向),w代表宽度像素数(左右方向)。

换言之,图像是像素列表(行)的列表(整张图)。每个像素由3个整数和1个可选浮点数组成:红色通道、绿色通道、蓝色通道以及alpha通道(可选浮点数)。红绿蓝(RGB)通道的取值范围是0到255。

为简化讨论,下文将仅涉及不含alpha通道的彩色图像。Alpha通道表示像素的透明度。图像也可以仅包含一个取值0到255的通道,即灰度图像(黑白图像)。但在本文中,我们将使用彩色图像。


import matplotlib as plt

pixel: tuple = (200, 100, 150)
plt.imshow([[list(pixel)]])

用纯Python制作图像

Python完全具备创建图像的能力。为了展示结果,我将使用matplotlib库,你可以通过以下命令安装它:


pip install matplotlib

创建像素:


from dataclasses import dataclass

@dataclass
class Pixel:
 red: int
 green: int
 blue: int
 # alpha: float = 1
  
pixel = Pixel(255,0,0)
pixel
# returns: 
# Pixel(red=255, green=0, blue=0, alpha=1)

创建图像:


from __future__ import annotations

from dataclasses import dataclass, astuple
from itertools import cycle
from typing import List

import matplotlib.pyplot as plt
import matplotlib.image as mpimg


@dataclass
class Pixel:
 red: int
 green: int
 blue: int
 # alpha: float = 1


pixel = Pixel(255,0,0)
pixel

marigold: Pixel = Pixel(234,162,33)
red: Pixel = Pixel(255,0,0)

Image = List[List[Pixel]]


def create_image(*colors: Pixel, blocksize: int = 10, squaresize: int = 9) -> Image:
 """ 用可配置的像素块制作一个正方形图像(宽度和高度相同).
 Args:
   colors (Pixel): 可迭代的颜色呈现顺序的参数。
   blocksize (int, optional): [description]. 默认10.
   squaresize (int, optional): [description]. 默认9.
 Returns:
   Image: 一幅漂亮的正方形图片!
 """
 img: list = []
 colors = cycle(colors)
 for row in range(squaresize):
  row: list = []
  for col in range(squaresize):
   color = next(colors) # 设置颜色
   for _ in range(blocksize):
    values: list[int] = list(astuple(color))
    row.append(values)
  [img.append(row) for _ in range(squaresize)] # 创建行高
 return img


if __name__ == '__main__':
 image = create_image(marigold, red)
 plt.imshow(image)

这就是渲染后的图像。在后台,数据结构如下所示:


[[[234, 162, 33],
 [234, 162, 33],
 [234, 162, 33],
 [234, 162, 33],
 [234, 162, 33],
 [234, 162, 33],
 [234, 162, 33],
 [234, 162, 33],
 [234, 162, 33],
 [234, 162, 33],
 [255, 0, 0],
 [255, 0, 0],
 [255, 0, 0],
 [255, 0, 0],
 [255, 0, 0],
 [255, 0, 0],
 [255, 0, 0],
 [255, 0, 0],
 [255, 0, 0],
 [255, 0, 0],
 [234, 162, 33],
 ...

主流Python图像处理库对比:OpenCV与Pillow

在Python中调整大小

在Python中编写图像缩放算法实际上涉及大量工作。

图像处理算法包含诸多细节,为此有人投入了巨大精力。例如重采样技术——在缩小后的图像中使用一个像素来代表周围的高分辨率像素群。图像处理是一个庞大的领域。如果你想一探究竟,可以查看Pillow的Image.py文件,它位于path/to/site-packages/PIL路径下。

其中还涉及抗锯齿和减少间隙等优化措施……内容极其丰富。我们是站在巨人的肩膀上,只需一行代码即可解决缩放问题。

如果你对图像处理的幕后机制感兴趣,我鼓励你深入研究“机器视觉”主题!这绝对是一个蓬勃发展的领域。 做到足够优秀,许多公司愿意为你的计算机视觉专业知识支付高薪。自动驾驶、物联网(IoT)、监控等领域,你提到的任何领域,基本上都依赖于图像处理(通常使用Python或C++)。

一个好的起点是查看scikit image库。

OpenCV

OpenCV可用于图像处理。它由C++编写,并移植到了Python。


import cv2

def resize(fp: str, scale: Union[float, int]) -> np.ndarray:
  """ 调整图像大小,保持其比例
  Args:
    fp (str): 图像文件的路径参数
    scale (Union[float, int]): 百分比作为参数。如:53
  Returns:
    image (np.ndarray): 按比例缩小的图片
  """  
  _scale = lambda dim, s: int(dim * s / 100)
  im: np.ndarray = cv2.imread(fp)
  width, height, channels = im.shape
  new_width: int = _scale(width, scale)
  new_height: int = _scale(height, scale)
  new_dim: tuple = (new_width, new_height)
  return cv2.resize(src=im, dsize=new_dim, interpolation=cv2.INTER_LINEAR)

interpolation参数的选项是cv2包中提供的标志之一:

INTER_NEAREST – 近邻插值 INTER_LINEAR – 双线性插值(默认使用) INTER_AREA – 利用像素区域关系重新采样。这可能是图像抽取的首选方法,但当图像被缩放时,它类似于INTER_NEAREST方法。 INTER_CUBIC – 一个大于4×4像素邻域的双三次插值 INTER_LANCZOS4 – 一个大于8×8像素邻域的Lanczos插值

返回结果后:


resized = resize("checkers.jpg", 50)
print(resized.shape)
plt.imshow(resized) # 也可以使用 cv2.imshow("name", image)

它完成了我们预期的工作。图像从900像素高、900像素宽,变为450×450(仍保留三个颜色通道)。由于Jupyter Lab中matplotlib的色彩渲染,上述截图看起来效果不佳。

Pillow

pillow库在Image类上提供了一个调整大小的方法。其参数包括:

size: (width, height) resample: 默认为BICUBIC。重采样算法所需的参数。 box: 默认为None。为一个4元组,定义了在参数(0,0,宽度,高度)内工作的图像矩形。 reducing_gap: 默认为None。重新采样优化算法,使输出看起来更好。

以下是函数代码:


from PIL import Image

def resize(fp: str, scale: Union[float, int]) -> np.ndarray:
  """ 调整图像大小,保持其比例
  Args:
    fp (str): 图像文件的路径参数
    scale (Union[float, int]): 百分比作为参数。如:53
  Returns:
    image (np.ndarray): 按比例缩小的图片
  """
  _scale = lambda dim, s: int(dim * s / 100)
  im = Image.open(fp)
  width, height = im.size
  new_width: int = _scale(width, scale)
  new_height: int = _scale(height, scale)
  new_dim: tuple = (new_width, new_height)
  return im.resize(new_dim)

使用Pillow的函数与OpenCV非常相似。唯一的区别是PIL.Image.Image类具有用于访问图像(宽度、高度)的属性size。

结果是:


resized = resize("checkers.jpg", 30.5)
print(resized.size)
resized.show("resized image", resized)

请注意,show方法如何打开操作系统的默认程序以查看图像的文件类型。

使用Click构建批量缩放命令行工具

创建命令行程序

现在我们已经拥有了调整图像大小的函数,是时候为其提供一个运行缩放的界面了。

调整单个图像的大小是可以的,但我们希望能够批量处理图像。

我们将要构建的接口将是最简单的形式:命令行实用程序。

Pallets项目是Flask背后的天才社区,也是一个Jinja模板引擎:Click(https://click.palletsprojects...。)


pip install click

Click是一个用于制作命令行程序的库。这比使用普通的argparse或在if __name__ == '__main__':中启动一些if-then逻辑要好得多。因此,我们将使用Click来装饰我们的图像调整器。

下面是从命令行调整图像大小的完整脚本!


""" resize.py
"""

from __future__ import annotations
import os
import glob
from pathlib import Path
import sys

import click
from PIL import Image


"""
文档:
  https://pillow.readthedocs.io/en/5.1.x/handbook/image-file-formats.html
"""
SUPPORTED_FILE_TYPES: list[str] = [".jpg", ".png"]


def name_file(fp: Path, suffix) -> str:
  return f"{fp.stem}{suffix}{fp.suffix}"


def resize(fp: str, scale: Union[float, int]) -> Image:
  """ 调整图像大小,保持其比例
  Args:
    fp (str): 图像文件的路径参数
    scale (Union[float, int]): 百分比作为参数。如:53
  Returns:
    image (np.ndarray): 按比例缩小的图片
  """
  _scale = lambda dim, s: int(dim * s / 100)
  im: PIL.Image.Image = Image.open(fp)
  width, height = im.size
  new_width: int = _scale(width, scale)
  new_height: int = _scale(height, scale)
  new_dim: tuple = (new_width, new_height)
  return im.resize(new_dim)


@click.command()
@click.option("-p", "--pattern")
@click.option("-s", "--scale", default=50, help="Percent as whole number to scale. eg. 40")
@click.option("-q", "--quiet", default=False, is_flag=True, help="Suppresses stdout.")
def main(pattern: str, scale: int, quiet: bool):
  for image in (images := Path().glob(pattern)):
    if image.suffix not in SUPPORTED_FILE_TYPES:
      continue
    im = resize(image, scale)
    nw, nh = im.size
    suffix: str = f"_{scale}_{nw}x{nh}"
    resize_name: str = name_file(image, suffix)
    _dir: Path = image.absolute().parent
    im.save(_dir / resize_name)
    if not quiet:
      print(
        f"resized image saved to {resize_name}.")
  if images == []:
    print(f"No images found at search pattern '{pattern}'.")
    return


if __name__ == '__main__':
  main()

命令行程序从入口点函数main运行。参数通过传递给click.option选项:

  • pattern采用字符串形式来定位与脚本运行目录相关的一个或多个图像。--pattern="../catpics/*.png将向上一级查找catpics文件夹,并返回该文件夹中具有.png图像扩展名的所有文件。
  • scale接受一个数字、浮点数或整数,并将其传递给resize函数。这个脚本很简单,没有数据验证。如果你添加到代码中,检查比例是一个介于5和99之间的数字(合理的缩小比例参数)。你可以通过-s "chicken nuggets"进行设置。
  • 如果不希望在程序运行时将文本输出到标准流,则quiet是一个选项参数。

从命令行运行程序:


python resize.py -s 35 -p "./*jpg"

结果:

$ py resize.py -p "checkers.jpg" -s 90 resized image saved to checkers_90_810x810.jpg.

正在检查文件夹:

$ ls -lh checkers* -rw-r--r-- 1 nicho 197609 362K Aug 15 13:13 checkers.jpg -rw-r--r-- 1 nicho 197609 231K Aug 15 23:56 checkers_90_810x810.jpg

不错!所以程序缩小了图像,给了它一个描述性的标签,我们可以看到文件大小从362KB变为231KB!

为了查看程序同时处理多个文件,我们将再次运行它:

$ py resize.py --pattern="checkers*" --scale=20 resized image saved to checkers_20_180x180.jpg. resized image saved to checkers_90_810x810_20_162x162.jpg.

文件系统输出:

$ ll -h checkers* -rw-r--r-- 1 nicho 197609 362K Aug 15 13:13 checkers.jpg -rw-r--r-- 1 nicho 197609 1.8K Aug 16 00:23 checkers_20_180x180.jpg -rw-r--r-- 1 nicho 197609 231K Aug 15 23:56 checkers_90_810x810.jpg -rw-r--r-- 1 nicho 197609 1.8K Aug 16 00:23 checkers_90_810x810_20_162x162.jpg

只要匹配到了模式,递归可以处理任意数量的图像。

将脚本打包为可执行工具

Click

Click 是一个神奇的工具。它可以包装一个函数并在一个模块中以“正常的方式”从一个if __name__ == '__main__'语句运行。(实际上,它甚至不需要这样做;你只需定义和装饰要运行的函数即可),但它真正的亮点在于将脚本作为包安装。

这是通过Python附带的setuptools库完成的。

这是我的setup.py。


from setuptools import setup

setup(
  name='resize',
  version='0.0.1',
  py_modules=['resize'],
  install_requires=[
    'click',
    'pillow',
  ],
  entry_points='''
    [console_scripts]
    resize=resize:main
  '''
)

使用以下命令生成可执行文件/包装包:


pip install -e .

总结与展望

结论

本教程进行了大量的研究:

  • 首先介绍了一些用于图像处理的第三方Python库。
  • 然后使用Python从头构建一个图像,以进一步了解图像的实际含义。
  • 然后,选择其中一个选项,并构建一个脚本,在保持图像比例的同时缩小图像。
  • 最后,把所有这些放在一个命令行实用程序中,通过click接受可配置的选项。

请记住,编写代码可能需要数小时或数天。但它只需几毫秒就可以运行。你制作的程序不必很大。任何一件能节省你的时间或让你产生更多产出的东西,都有可能为你的余生服务!

原文链接:

https://towardsdatascience.co...

以上就是如何使用Python调整图像大小的详细内容,更多关于Python调整图像大小的资料请关注本站其它相关文章!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。