首页
开发技巧正文内容

利用 Python 实现自动化图像裁剪:简单高效的工作流程

2024年11月11日
阅读时长 4 分钟
阅读量 9
利用 Python 实现自动化图像裁剪:简单高效的工作流程

手动裁剪数百张图像?不用了!使用 Python 和 OpenCV,您可以自动检测图像的焦点并在几秒钟内将其裁剪为完美的 16:9。本指南向您展示如何节省数小时的编辑时间 - 只需几行代码,您就可以开始了!

这项工作涉及许多步骤,我尽量将其自动化。今天我想告诉您一个特别容易用 Python 自动化的步骤。也就是裁剪图像。

假设我们有这张图片:

我想要的是一张以骑手为焦点、保留图像宽度的 16:9 图像。这个过程很复杂,您可以使用昂贵的解决方案,比如 Photoshop,或者 Krita、Paint.NET 或 IrfanView 来处理。

但是如果您有 1000 个文件需要处理呢?1000x5 分钟 = 大量时间。

所以让我们自动化它。我的最初想法如下:

使用自动裁剪,在固定位置裁剪:

微笑,抬眉毛,停止微笑 - 模特脸部

完美!

我的下一个想法必须超越可能性,因为我不知道如何实现:

  1. 找到图像中最重要的部分在哪里

  2. 围绕它裁剪 16:9

后者很容易,前者不是吗?但是有 Python、numpy、OpenCV、PIL,为什么不试试呢?

所需库:

  • OpenCV:这个库处理图像处理,并且是我们操纵图像的主要工具。使用 pip install opencv-python 安装它。

  • PIL(或 Pillow):Python Imaging Library 用于额外的图像处理任务。使用 pip install pillow 安装它。

  • NumPy:一个强大的用于数值计算的库,与 OpenCV 完美集成。使用 pip install numpy 安装它。

基本上是免费的 Photoshop :)

经过一些研究,我发现您可以通过模糊处理、灰度化、检测边缘、查找轮廓和识别最大轮廓来实现。

让我们来看看代码中最重要的几行,最终的脚本将在本文末尾:

cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

这行将图像转换为灰度:

接下来,我们必须稍微模糊化图像,以减少后续处理的 CPU 强度:

blurred = cv2.GaussianBlur(gray, (11, 11), 0)

这里需要注意的是,通过增加 (11, 11) 可以增加模糊度,使脚本在查找图像中心时更准确或更不准确。但这些数字必须是奇数。

接下来,我们检测边缘:

edges = cv2.Canny(blurred, 50, 150)

现在,我们找到轮廓。轮廓就像是由边缘形成的形状。所以您可以想象三角形有 3 条边形成一个轮廓:

cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

当我们有了轮廓,我们只需获取最大的轮廓,令人惊讶的是:

是的,最长的轮廓在马腿之间。所以我们画一个边界矩形:

瞧 - 图像中最重要的元素(某种程度上)。

我还将其裁剪为 16:9 并使用三分法则,但这只是数学问题,所以我不会详细介绍。基本上,您取主要主题的中心,围绕它绘制一个 16:9 的矩形,并裁剪图像,同时将其与摄影三分法则对齐,使其看起来令人印象深刻。

一款科幻 Python 卡牌游戏,还有幻想和中性版本。现成或数字打印

同样,我的Python Deck聚焦于 Python 的基本概念 - 捕捉最重要的“中心”,然后精确地定位每个原则,以便您可以有效地学习。就像构图一样,关键是将 Python 最强大的思想放在产生最大影响的地方。

仅供参考,这是两个三分之二的样子:

三分法则是一种摄影指导原则,将图像分为 3x3 网格,帮助您沿着这些线或它们的交点放置重要元素。通过将主要主题与这些点对齐,您可以创造出一个平衡、视觉上吸引人的构图,自然地吸引观众的目光。

以及计算出的裁剪区域:

结果:

漂亮对吧?

如果我们尝试在其他图像上运行它,我们会得到不同的结果:

并不是总是按您想象的那样有效,因为最重要的部分不清楚。这只是一个数学假设:

但它效果相当不错,尤其是当您调整数字时。因此,如果您有很多文件并希望自动裁剪它们 - 保持摄影三分法则并专注于最重要的部分 - 这是完全可行的。

我正在写的桌垫之一

如果您喜欢能够提高工作效率的技术工具,请查看我的桌垫。它们充满了编程快捷方式和提示,旨在让您随手可得地保留基本要点 - 就像这个脚本对图像裁剪所做的那样!

啊,这就是脚本:

import cv2
from PIL import Image
import numpy as np
from pathlib import Path

# 定义源文件夹和目标文件夹
input_folder = "demo"  # 图像源文件夹
output_folder = "demo/output"  # 处理后图像的目标文件夹```markdown
def crop_important_region(image_path, output_path):
    # 加载图像
    image = cv2.imread(str(image_path))
    if image is None:
        raise ValueError(f"无法加载图像:{image_path}")

    # 设置保存步骤的文件名前缀
    base_name = image_path.stem  # 不带扩展名的基本名称

    # 步骤 1-1:保存原始图像
    cv2.imwrite(str(output_path / f"{base_name}_1-1-original.jpg"), image)

    # 获取图像尺寸
    img_height, img_width = image.shape[:2]

    # 根据图像宽度计算16:9宽高比的高度
    target_height = int(img_width * 9 / 16)

    # 步骤 1-2:转换为灰度并保存
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    cv2.imwrite(str(output_path / f"{base_name}_1-2-grayscale.jpg"), gray)

    # 步骤 1-3:应用更强的高斯模糊以减少噪点和细节,并保存
    blurred = cv2.GaussianBlur(gray, (11, 11), 0)  # 增加内核大小以加强模糊效果
    cv2.imwrite(str(output_path / f"{base_name}_1-3-blurred.jpg"), blurred)

    # 步骤 1-4:使用Canny边缘检测器检测边缘并保存
    edges = cv2.Canny(blurred, 50, 150)
    edges_colored = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)  # 将边缘转换为BGR以便更好地可视化
    edges_colored[np.where((edges_colored == [255, 255, 255]).all(axis=2))] = [0, 0, 255]  # 使边缘呈红色
    cv2.imwrite(str(output_path / f"{base_name}_1-4-edges.jpg"), edges_colored)

    # 步骤 1-5:查找轮廓
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # 找到最大的轮廓(假设为主要对象)
    largest_contour = max(contours, key=cv2.contourArea)

    # 在原始图像上仅可视化最大边缘,以更清晰地显示高分辨率
    largest_edge_image = image.copy()
    cv2.drawContours(largest_edge_image, [largest_contour], -1, (0, 255, 0), 10)  # 绿色,10像素厚度
    cv2.imwrite(str(output_path / f"{base_name}_1-5-largest-edge.jpg"), largest_edge_image)

    # 获取围绕最大轮廓的边界框
    x, y, w, h = cv2.boundingRect(largest_contour)

    # 计算最大轮廓的中心
    center_x = x + w // 2
    center_y = y + h // 2

    # 在原始图像上可视化边界框,填充为蓝色,并在中心处画交叉线
    bounding_box_image = image.copy()
    cv2.rectangle(bounding_box_image, (x, y), (x + w, y + h), (255, 0, 0), -1)  # 用蓝色填充边界框
    cv2.line(bounding_box_image, (center_x, y), (center_x, y + h), (255, 0, 0), 10)  # 中心垂直线
    cv2.line(bounding_box_image, (x, center_y), (x + w, center_y), (255, 0, 0), 10)  # 中心水平线
    cv2.imwrite(str(output_path / f"{base_name}_1-6-bounding-box.jpg"), bounding_box_image)

    # 步骤 1-7:绘制三分之一规则网格并显示两分之一叠加
    thirds_image = image.copy()
    # 绘制垂直三分之一线(黄色,更粗的线以便查看)
    cv2.line(thirds_image, (img_width // 3, 0), (img_width // 3, img_height), (0, 255, 255), 8)
    cv2.line(thirds_image, (2 * img_width // 3, 0), (2 * img_width // 3, img_height), (0, 255, 255), 8)
    # 绘制水平三分之一线
    cv2.line(thirds_image, (0, img_height // 3), (img_width, img_height // 3), (0, 255, 255), 8)
    cv2.line(thirds_image, (0, 2 * img_height // 3), (img_width, 2 * img_height // 3), (0, 255, 255), 8)
    # 用半透明绿色叠加图像的三分之二
    overlay = thirds_image.copy()
    cv2.rectangle(overlay, (0, 0), (img_width, int(2 * img_height // 3)), (0, 255, 0), -1)
    cv2.addWeighted(overlay, 0.3, thirds_image, 0.7, 0, thirds_image)
    cv2.imwrite(str(output_path / f"{base_name}_1-7-thirds-and-two-thirds.jpg"), thirds_image)

    # 计算裁剪的y坐标,使重要区域居中在16:9裁剪的下部三分之一内
    crop_y = max(0, center_y - (2 * target_height) // 3)
    crop_y = min(crop_y, img_height - target_height)  # 确保裁剪在图像范围内

    # 步骤 1-8:在原始图像上显示裁剪区域
    crop_area_image = image.copy()
    cv2.rectangle(crop_area_image, (0, crop_y), (img_width, crop_y + target_height), (255, 0, 255), 8)  # 品红色边框
    cv2.imwrite(str(output_path / f"{base_name}_1-8-crop-area.jpg"), crop_area_image)

    # 步骤 1-9:将图像裁剪到指定宽度和目标高度
    cropped_image = image[crop_y:crop_y + target_height, 0:img_width]

    # 保存带后缀的最终裁剪图像
    output_image_path = output_path / f"{base_name}_1-9-cropped.jpg"
    output_image = Image.fromarray(cv2.cvtColor(cropped_image, cv2.COLOR_BGR2RGB))
    output_image.save(output_image_path)

    print(f"已处理并保存 {image_path.name}")

def process_all_images_in_folder():
    # 设置输入和输出目录
    input_dir = Path(input_folder)
    output_dir = Path(output_folder)
    output_dir.mkdir(exist_ok=True)

    # 处理输入目录中的每个图像文件
    for image_path in input_dir.glob("*.*"):
        if image_path.is_file() and image_path.suffix.lower() in {'.jpg', '.jpeg', '.png', '.webp'}:
            try:
                crop_important_region(image_path, output_dir)
            except Exception as e:
                print(f"处理 {image_path.name} 时出错:{e}")

# 在输入文件夹中运行脚本
process_all_images_in_folder()

完成操作。

免责声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

相关文章

探索多种软件架构模式及其实用应用
2024年11月22日19:06
本文深入探讨了多种软件架构模式,包括有界上下文、边车模式、发布-订阅模式、应用网关、微服务、命令职责分离(CQRS)等,介绍了它们的优点、使用场景以及具体应用实例。文章强调根据具体项目需求和团队能力选择最合适的架构,以构建高效和可维护的解决方案,同时展示了各架构模式间的综合应用,提供了丰富的案例和技术细节。
15个高级Python快捷键助您更快编程
2024年11月21日07:02
本文分享了 15 个高级的 Python 编程快捷键,包括上下文管理器、行内字典合并、函数参数解包、链式比较、dataclasses、海象运算符、反转列表、备忘录缓存、splitlines、enumerate、字典推导、zip 用于并行迭代、itertools.chain 扁平化列表、functools.partial 部分函数和 os.path 文件路径管理等,帮助开发者提高编程效率和代码简洁性。
揭示网页开发的 11 个迷思:停止相信这些误区
2024年11月19日22:05
网页开发充满误解,这篇博文针对11个常见迷思进行揭秘。包括网站开发后不需更新、需掌握所有技术、AI会取代开发者等。强调持续学习、专业化、用户体验的重要性,澄清误区如多任务处理的必要性和最新技术的必需性。文章提醒开发者注重实用而非追求完美代码,以务实态度面对开发工作。
你知道 CSS 的四种 Focus 样式吗?
2024年11月18日21:41
本文介绍了四种 CSS focus 样式::focus、:focus-visible、:focus-within 以及自定义的 :focus-visible-within,帮助提升网站用户体验。:focus 样式应用于被选中元素;:focus-visible 仅在键盘导航时显示;:focus-within 用于父元素;自定义 :focus-visible-within 结合两者效果。合理运用这些样式能使网站更方便键盘用户导航。
利用 Python 实现自动化图像裁剪:简单高效的工作流程
2024年11月11日20:49
使用 Python 和 OpenCV 自动裁剪图像,轻松实现 16:9 的完美构图。这个指南介绍了如何通过代码进行灰度化、模糊处理和边缘检测,最终识别出最重要的部分进行裁剪。特别适合需要批量处理图像的情况,节省大量时间。
每位资深前端开发人员都应了解的 TypeScript 高级概念
2024年11月11日02:07
资深前端开发者应了解 TypeScript 的高级概念,如联合类型、交叉类型、类型保护、条件类型、映射类型、模板字面量类型和递归类型。这些特性可提升代码的可维护性和可扩展性,确保在开发复杂应用时实现更高的类型安全性和效率。