物理のバス停 by salt22g

とある物理学見習いの備忘録。

pythonのglobで末端のファイル名だけ取得したい

pythonのglob.globによるファイル名の一括取得。便利ですよね。
glob君に任せ切りにしていたら一瞬迷ったのでメモとして残します。
全然大した話ではないのですし、もっと良いやり方があるかもしれません。

2020/05/17 15:20 追記

os.path.basename()

これで行けるらしい。俺がアホだった。

cv2.imwrite(new_dir + os.path.basename(file1), merge_img)

問題

こんな処理をしたい時がありました。

  • 二つのファイルに同じ名前だが異なる処理をした画像がある。
  • これらのファイルから同じ名前の画像を読み込み横につなげる。
  • 別のファイルに元と同じ名前で書き出す。

ファイル構造はこんな感じ。名前は連番ではない場合もあります。

current_dir/
  ┠ gattai.py
  ┠ images_A/
  ┃  ┠ abcdefg.png
  ┃  ┠ hijk.png
  ┃  ┠ lmnopq.png
  ┃  ┠ 以下略
  ┠ images_B/
  ┃  ┠ abcdefg.png
  ┃  ┠ hijk.png
  ┃  ┠ lmnopq.png
  ┃  ┠ 以下略

↑を↓にしたい。

current_dir/
  ┠ gattai.py
  ┠ images_A/ #略
  ┠ images_B/ #略
  ┠ images_C/ #自分で名前をつけて作成
  ┃  ┠ abcdefg.png #処理されて合体した画像
  ┃  ┠ hijk.png
  ┃  ┠ lmnopq.png
  ┃  ┠ 以下略

最初は何も考えずこんなコードを書きました。

import numpy as np
import cv2
import os
import glob
import sys
import re

args = sys.argv
filename1 = args[1] #images_A
filename2 = args[2]  #images_B
outname = args[3]   #images_C

if __name__ == "__main__":

    new_dir =  outname + "/"
    os.makedirs(new_dir, exist_ok=True) #出力用のファイルを生成

    files1 = glob.glob(filename1 + "/*.png") #ファイル内の画像のパスを取得
    files1.sort()
    files2 = glob.glob(filename2 + "/*.png")
    files2.sort()


    for file1, file2 in zip(files1, files2): #読み込む画像パスを名前をループで取得
        img1 = cv2.imread(file1)
        img2 = cv2.imread(file2)
        images = [img1, img2]
        merge_img = np.concatenate(images, axis=1)
        cv2.imwrite(new_dir + file1, merge_img)

実行してみると…

python3 gattai.py images_A images_B images_C

実はこの部分がまずい。

new_dir + file1 ## => images_C/images_A/hogehoge.png

globで取得したfile1には
images_A/hogehoge.png
というように相対パスが全て書き込まれます。

os.makedirs(new_dir, exist_ok=True)

で作成したのはimages_Cまでなのでその中のimages_Aは作成されておらず画像は保存できません。

f:id:salt22g:20200517053640p:plain
模式図(わかりづらい)

解決

pythonの文字列分割機能を使って解決しました。
qiita.com

phrase = "If you can dream it you can do it"
# 「split」使用 .split(区切り文字, 分割数)
print(phrase.split()) ## => ["If","you","can","dream","it","you","can","do","it"]

word = "HelloWorld"
# 「rsplit」使用 .rsplit(区切り文字, 分割数)
print(word.rsplit("l",1)) ## => ['HelloWor', 'd']

今回globで取得したパスは
/images_A/hogehoge.png
取得したいのはhogehoge.pngの部分なので、"/"で区切った末端の部分を取れば良いですね。

phrase.split("/") ## => ["", "images", "hogehoge.png"]
phrase.split("/")[-1] ## => ["hogehoge.png"]

ということで最後のcv2.imwriteをこのように書き換えました。

cv2.imwrite(new_dir + file1.split("/")[-1], merge_img)

これで当初の狙い通りの出力になりました。めでたし。