How can I use os.walk and PIL to save an edited image in a new directory but with the same subdirectories as in the original source?

huangapple go评论69阅读模式
英文:

How can I use os.walk and PIL to save an edited image in a new directory but with the same subdirectories as in the original source?

问题

我试图使用PIL编辑指定目录包括子目录中的所有图像然后将编辑后的版本保存在不同位置但具有相同的子目录结构下

例如 输入:

根目录
|
|---------子目录 1
| |---图像_1
| |---图像_2
| |---图像_3
|
|---------子目录 2
| |---图像_1
| |---图像_2

输出:
{指定目录}
|
|---------子目录 1
| |---编辑后的图像_1
| |---编辑后的图像_2
| |---编辑后的图像_3
|
|---------子目录 2
| |---编辑后的图像_1
| |---编辑后的图像_2


子目录具有相同的名称,并在运行os.walk时创建。

我将其构建为一个函数,以便稍后在Linux终端中调用该函数时更改选项。

```python
from PIL import Image, ImageDraw, ImageOps
import os
from os.path import isfile, join

def editimage(image_directory="image_directory",
               version="B", #指定要进行哪个版本的ImageDraw编辑
               resize=False, #选择是否调整图像大小
               new_size=(800,428), #选择新大小
               output_directory="output_directory"):

    for directory, subdirs, files in os.walk(image_directory):
        for dirname in subdirs:
            if not os.path.exists(output_directory + dirname):
                os.mkdir(output_directory + image_directory + dirname) #这正是我想要的

    for directory, subdirs, files in os.walk(image_directory):
        for f in files:
            if f.endswith('.JPG'):
                img = Image.open(directory + f)
                draw= ImageDraw.Draw(img)
                if version=="A":
                    draw.ellipse((1823, 1472, 2033, 1536), fill = (0,0,0), outline=None) 
                    draw.rectangle((0, 0, 2048, 32), fill = (0,0,0), outline=None) 
                    draw.rectangle((0, 1504, 2048, 1536), fill = (0,0,0), outline=None) 
                    img=ImageOps.pad(img, size=(2731,1536), color="grey")
                elif version=="B":
                    draw.rectangle((0, 1231, 2304, 1296), fill = (0,0,0), outline=None)
                if resize==True:
                    img = img.resize(new_size)
                else:
                    pass
                img.save(output_directory + "/" + f + ".png")

这样用户可以输入:

editimage("输入路径/", version="A", resize= False, output_directory="输出路径/")

我尝试过使用img.save(output_directory + subdirs + ".png"或为此创建一个变量,但Python返回:TypeError: can only concatenate list (not "str") to list,或者希望在输出目录中包含输入目录。它似乎没有遍历每个子目录。

我之前使用了

    imagelist = [join(directory, f)
                 for directory, subdirs, files in os.walk(image_directory)
                 for f in files
                 if isfile
                 if f.endswith('.JPG')]

    for f in imagelist:
        img = Image.open(f) #等等..

来构建读取和编辑图像,但然后img.save(f)会保留整个路径,并且无法将图像保存到新位置。

函数的其余部分完全正常工作,如果我在img.save行中删除output_directory参数,则会覆盖原始目录(包括子目录)中的图像。直到意识到未使用输出目录时,我没有任何问题。

由于版本A的图像在文件夹之间没有唯一的名称,因此我需要能够重构目录路径。

我该如何修复这个问题?
谢谢!


<details>
<summary>英文:</summary>

I&#39;m trying to edit all images within a specified directory (and subdirs) using PIL, and then save the edited versions in a different location but under the same subdirectory structure.

ie. Input: 

Root directory
|
|---------Subdir 1
| |---Image_1
| |---Image_2
| |---Image_3
|
|---------Subdir 2
| |---Image_1
| |---Image_2

And Output:
{Specified directory}
|
|---------Subdir 1
| |---Edited_Image_1
| |---Edited_Image_2
| |---Edited_Image_3
|
|---------Subdir 2
| |---Edited_Image_1
| |---Edited_Image_2


Where the subdirs share same names, and are created when os.walk is run.

I&#39;ve constructed this as a function so I can later run it in a Linux terminal with options changed upon calling the function. 

from PIL import Image, ImageDraw, ImageOps
import os
from os.path import isfile, join

def editimage(image_directory="image_directory",
version="B", #Specify which version of ImageDraw edit to do
resize=False, #choose whether or not to resize image
new_size=(800,428), #choose new size
output_directory="output_directory"):

for directory, subdirs, files in os.walk(image_directory):
    for dirname in subdirs:
        if not os.path.exists(output_directory + dirname):
            os.mkdir(output_directory + image_directory + dirname) #This does exactly what I want it to do

for directory, subdirs, files in os.walk(image_directory):
    for f in files:
        if f.endswith(&#39;.JPG&#39;):
            img = Image.open(directory + f)
            draw= ImageDraw.Draw(img)
            if version==&quot;A&quot;:
                draw.ellipse((1823, 1472, 2033, 1536), fill = (0,0,0), outline=None) 
                draw.rectangle((0, 0, 2048, 32), fill = (0,0,0), outline=None) 
                draw.rectangle((0, 1504, 2048, 1536), fill = (0,0,0), outline=None) 
                img=ImageOps.pad(img, size=(2731,1536), color=&quot;grey&quot;)
            elif version==&quot;B&quot;:
                draw.rectangle((0, 1231, 2304, 1296), fill = (0,0,0), outline=None)
            if resize==True:
                img = img.resize(new_size)
            else:
                pass
            img.save(output_directory + &quot;/&quot; + f + &quot;.png&quot;)

This would let the user enter:

editimage("Path/to/input/", version="A", resize= False, output_directory="Path/to/output/")


I&#39;ve tried using `img.save(output_directory + subdirs + &quot;.png&quot;` or creating a variable for that, but Python returns: `TypeError: can only concatenate list (not &quot;str&quot;) to list`, or otherwise wants the input directory within the output directory. It doesn&#39;t seem to walk every subdirectory.

I previously used 
imagelist = [join(directory, f)
             for directory, subdirs, files in os.walk(image_directory)
             for f in files
             if isfile
             if f.endswith(&#39;.JPG&#39;)]

for f in imagelist:
    img = Image.open(f) #etc..
to construct read and edit the images, but then `img.save(f)` keeps the entire path and cannot save the image to a new location.

The rest of the function works perfectly, and will overwrite images within the original directory (and subdirs) if I remove the output_directory argument in the img.save line. I had no issues until realising that the output directory wasn&#39;t being used.

Because the version A set of images are not named uniquely between folders, I need to be able to reconstruct the directory paths.

How can I fix this?
Thanks!


</details>


# 答案1
**得分**: 1

以下是代码的翻译部分:

```python
# 导入需要的库
from pathlib import Path
from PIL import Image, ImageDraw

# 定义函数 editimage
def editimage(
    image_directory="image_directory",
    version="B",  # 指定要进行的 ImageDraw 编辑版本
    resize=False,  # 选择是否调整图像大小
    new_size=(800, 428),  # 选择新的图像尺寸
    output_directory="output_directory",
):
    for jpg_file in image_directory.rglob("*.jpg"):
        # 将输入文件路径转换为相应的输出文件路径
        relative_path = jpg_file.relative_to(image_directory)
        output_file_path = output_directory / relative_path

        # 如果输出目录不存在,则创建
        output_file_path.parent.mkdir(parents=True, exist_ok=True)

        # 对图像进行标注并保存(简化示例)
        with Image.open(jpg_file) as img:
            draw = ImageDraw.Draw(img)
            draw.ellipse((0, 0, 300, 300), fill=(255, 0, 0), outline=None)
            img.save(output_file_path)

# 调用 editimage 函数
editimage(
    image_directory=Path("Path/to/input/"),
    version="A",
    resize=False,
    output_directory=Path("Path/to/output/"),
)

请注意,我已经将代码中的 HTML 实体编码(如 &quot;)还原为普通的引号。

英文:

The function below will create a new directory for annotated images with the same tree structure as the input directory. I used pathlib from the core Python library even though you asked how to use os.walk(). I like its object-oriented methods and find that the code is cleaner. PEP428 provides more detail on the rationale behind pathlib.

from pathlib import Path

from PIL import Image, ImageDraw


def editimage(
    image_directory=&quot;image_directory&quot;,
    version=&quot;B&quot;,  # Specify which version of ImageDraw edit to do
    resize=False,  # choose whether or not to resize image
    new_size=(800, 428),  # choose new size
    output_directory=&quot;output_directory&quot;,
):
    for jpg_file in image_directory.rglob(&quot;*.jpg&quot;):
        # Translate the input file path to the corresponding output file_path
        relative_path = jpg_file.relative_to(image_directory)
        output_file_path = output_directory / relative_path

        # Create the output directory if it doesn&#39;t exist
        output_file_path.parent.mkdir(parents=True, exist_ok=True)

        # Annotate the image and save it (simplified example)
        with Image.open(jpg_file) as img:
            draw = ImageDraw.Draw(img)
            draw.ellipse((0, 0, 300, 300), fill=(255, 0, 0), outline=None)
            img.save(output_file_path)


editimage(
    image_directory=Path(&quot;Path/to/input/&quot;),
    version=&quot;A&quot;,
    resize=False,
    output_directory=Path(&quot;Path/to/output/&quot;),
)

huangapple
  • 本文由 发表于 2023年7月18日 04:18:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76707825.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定