zoukankan      html  css  js  c++  java
  • 使用Python调整图像大小

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

    可以说,每一个“使用计算机的人”都需要在某个时间点调整图像的大小。MacOS的预览版可以做到,WindowsPowerToys也可以。

    本文使用Python来调整图像大小,幸运的是,图像处理和命令行工具是Python的两个特长。

    本文旨在向你展示三件事:

    1. 图像的基本概念。

    2. 用于操作图像的Python库。

    3. 你可以在自己的项目中使用本文的代码。

    我们要构建的命令行程序可以一次调整一个或多个图像文件的大小。

    创建图像

    在这个例子中,我们将创建我们自己的图像,而不是找到一个真正的图像来操纵。

    为什么?事实上,创造图像是一个很好的方式来说明一个图像实际上是什么。这个调整大小的程序在Instagram上也同样适用。

    那么,什么是图像?在Python数据术语中,图像是int元组的列表。

    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中调整大小

    在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_ 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包中提供的flags之一:

    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_ 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类具有用于访问图像(宽度、高度)的属性大小。

    结果是:

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

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

    创建命令行程序

    现在我们有了一个调整图像大小的函数,现在是时候让它有一个运行调整大小的用户界面了。

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

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

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

    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_ 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命令的情况下调用脚本。另外,如果你将新的可执行文件添加到路径中的文件夹中,你可以从计算机上的任何位置调用此程序,如resize -p *jpg -s 75

    结论

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

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

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

    资源

    原文链接:https://towardsdatascience.com/how-to-resize-images-using-python-8aaba74602ed

    欢迎关注磐创AI博客站:
    http://panchuang.net/

    sklearn机器学习中文官方文档:
    http://sklearn123.com/

    欢迎关注磐创博客资源汇总站:
    http://docs.panchuang.net/

  • 相关阅读:
    Data Base Oracle 常用命令
    ASP.NET Core ASP.NET Core+MVC搭建及部署
    Hadoop 之 MapReduce 框架演变详解
    计算机网络: IP地址,子网掩码,默认网关,DNS服务器详解
    Linux系统基本网络配置之ifconfig命令
    Linux-eth0 eth0:1 和eth0.1关系、ifconfig以及虚拟IP实现介绍
    Linux 中将用户添加到组的指令
    几种常见的Shell
    常见的Shell
    Linux(CentOS6.5)下创建新用户和组,并制定用户和组ID
  • 原文地址:https://www.cnblogs.com/panchuangai/p/13733129.html
Copyright © 2011-2022 走看看