【问题标题】:How to loop through a folder of subfolders and read the images inside each subfolder?如何循环遍历子文件夹并读取每个子文件夹中的图像?
【发布时间】:2022-11-26 20:17:31
【问题描述】:

目前,我有一个能够无错误运行的 python 程序。然而,它只能遍历图像的子文件夹并提取每幅图像中 468 个面部标志的 x、y、z 坐标。我想编辑它,这样程序将循环遍历许多子文件夹并读取每个子文件夹中的许多图像。 “路径”函数中需要说明什么,下面我的代码中需要编辑什么?该文件夹命名为“nopain”,子文件夹命名为“1、2、3 等...”

import os
import cv2
import mediapipe as mp
import time
from os import listdir
import matplotlib.pyplot as plt
from pathlib import Path
import glob
import numpy
path = glob.glob("C:/Users/Downloads/Mac master DB_no overlap/nopain/1/*.png")
fh = open('out.txt', 'w')
for file in path:
    img = cv2.imread(file)
    mpDraw = mp.solutions.drawing_utils
    mpFaceMesh = mp.solutions.face_mesh
    facemesh = mpFaceMesh.FaceMesh(max_num_faces=1)
    drawSpec = mpDraw.DrawingSpec(thickness=1, circle_radius=2)
    rgb_image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    result = facemesh.process(rgb_image)
    if result.multi_face_landmarks:
        for faceLms in result.multi_face_landmarks:
            mpDraw.draw_landmarks(img, faceLms, mpFaceMesh.FACEMESH_CONTOURS,
            drawSpec, drawSpec)
            for lm in faceLms.landmark:
                print(lm, file, file = fh)
cv2.imshow("image", img)
cv2.destroyAllWindows()
fh.close()

【问题讨论】:

  • 这将是难以置信的 CPU 密集型。您是否考虑过多处理?如果没有,你应该。另外,你为什么打电话cv2.imshow()在最后处理的图像上?似乎有点奇怪
  • @OldBill 抱歉,我还没有尝试过多处理,那怎么办?还有我应该把“cv2.imshow()”改成什么?
  • 请不要通过删除其内容来破坏您的帖子。

标签: python mediapipe


【解决方案1】:

您需要一个递归 glob,如下所示。注意双星号

path = glob.glob("C:/Users/Downloads/Mac master DB_no overlap/nopain/**/*.png", recursive=True)

【讨论】:

  • 适用于具有 25 个子文件夹的小文件夹,但不能完全运行具有 2000 多个子文件夹的大文件夹
  • @Mohamed 它以什么方式不起作用?您是否也考虑过 os.walk()?
  • 该程序只能打印到第 26 个子文件夹。但是,文件夹中总共有近2000+个子文件夹
  • @Mohamed 你是在暗示 glob 只是停止返回值,引发异常,什么?
  • 没有引发异常,但是是的,由于某种原因,glob 在第 26 个子文件夹之后停止返回值
【解决方案2】:

检查 pathlib 模块https://docs.python.org/3/library/pathlib.html

这应该有效:

import pathlib
path = pathlib.Path("C:/Users/Downloads/Mac master DB_no overlap/nopain").rglob("*.png")

例如 我正在使用 bash 生成一些虚拟目录和文件来展示它是如何工作的。

$ mkdir -p {1,2,3,4}/dir{1,2,3}
$ touch {1,2,3,4}/dir{1,2,3}/img.png
$ touch {1,2,3,4}/img1.png
$ tree
.
├── 1
│   ├── dir1
│   │   └── img.png
│   ├── dir2
│   │   └── img.png
│   ├── dir3
│   │   └── img.png
│   └── img1.png
├── 2
│   ├── dir1
│   │   └── img.png
│   ├── dir2
│   │   └── img.png
│   ├── dir3
│   │   └── img.png
│   └── img1.png
├── 3
│   ├── dir1
│   │   └── img.png
│   ├── dir2
│   │   └── img.png
│   ├── dir3
│   │   └── img.png
│   └── img1.png
└── 4
    ├── dir1
    │   └── img.png
    ├── dir2
    │   └── img.png
    ├── dir3
    │   └── img.png
    └── img1.png

这是使用 pathlib 模块的输出:

>>> import pathlib
>>> for p in pathlib.Path("./").rglob("*.png"):
...     print(p)
...
2/img1.png
2/dir3/img.png
2/dir1/img.png
2/dir2/img.png
1/img1.png
1/dir3/img.png
1/dir1/img.png
1/dir2/img.png
3/img1.png
3/dir3/img.png
3/dir1/img.png
3/dir2/img.png
4/img1.png
4/dir3/img.png
4/dir1/img.png
4/dir2/img.png

【讨论】:

  • 我必须手动为 2000 多个子文件夹一一命名吗?
  • 不 - 我只生成了一个示例,说明您的文件夹结构可能看起来像演示。 (我更新了我的答案以包括您应该尝试的块)
【解决方案3】:

而不是像其他人建议的那样使用递归 glob,我建议使用以下代码,以便您可以访问当前正在处理的子文件夹的名称 - 这可能对您用地标命名输出文件很有用。

base_path = Path("C:/Users/Downloads/Mac master DB_no overlap/nopain")
subfolders = [item for item in base_path.iterdir() if item.is_dir()]
for subfolder in subfolders:
    with open(f'out_{str(subfolder.name)}.txt', 'w') as fh:
        print(f"Working on subfolder '{str(subfolder)}'...")
        for file in subfolder.glob("*.png"):
            img = cv2.imread(str(file))
            mpDraw = mp.solutions.drawing_utils
            mpFaceMesh = mp.solutions.face_mesh
            facemesh = mpFaceMesh.FaceMesh(max_num_faces=1)
            drawSpec = mpDraw.DrawingSpec(thickness=1, circle_radius=2)
            rgb_image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            result = facemesh.process(rgb_image)
            if result.multi_face_landmarks:
                for faceLms in result.multi_face_landmarks:
                    mpDraw.draw_landmarks(img, faceLms, mpFaceMesh.FACEMESH_CONTOURS,
                    drawSpec, drawSpec)
                    for lm in faceLms.landmark:
                        print(lm, file, file=fh)
        cv2.imshow("image", img)
        cv2.destroyAllWindows()

使用此代码,带有地标的输出文件将被适当命名:例如,out_1.txt 用于子文件夹 1out_2.txt 用于子文件夹 2,等等。

您还可以将 out.txt 保存到子文件夹本身。因为subfolder 是一个pathlib.Path 对象,所以使用with open(str(subfolder / 'out.txt'), 'w') as fh: 行。

【讨论】:

  • fh = open(f'out_{str(subfolder)}.txt', 'w') OSError: [Errno 22] 无效参数: 'out_C:\Users\\Downloads\Mac master DB_no overlap\nopain\1.txt'
  • 收到那个错误@Raj K
  • 抱歉,应该是fh = open(f'out_{str(subfolder.name)}.txt', 'w')str(subfolder) 返回完整路径,但在本例中我们只需要名称(例如,1)。我会更新我的答案来解决这个问题。
  • 不知道为什么我会收到以下错误:“img = cv2.imread(file) TypeError:无法将对象转换为‘filename’的‘str’”
  • 我的错!这是因为 file 是一个 pathlib.Path 对象,所以它需要转换为 str - 我的另一个疏忽。我将更新我的答案,它应该使用 img = cv2.imread(str(file)) 行。
猜你喜欢
  • 1970-01-01
  • 2021-10-16
  • 2016-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-03
相关资源
最近更新 更多