■ 공부/Python

[python] 사진파일 분류 프로그램(메타데이터 추출)

J U N E 2024. 3. 24. 15:53

과제로 사진파일 분류해 주는 프로그램을 만들어 보기로 했다.

아무래도 사진 찍으러 많이 돌아다니다보니

그 많은 파일들 정리하기가 여간 귀찮은 일이 아닐 수 없다.

조금만 신경꺼도 금새 불어나 있는 나의 사진갤러리.......

프로그래밍 공부를 시작할 때 언젠간 만들어보리라 다짐했던 그것을

이제야 해 보게 되는구만. 조와써

 

 

일단 확장성(?) 때문에 원본폴더와 저장할 폴더는 직접 지정하는 걸로 만들긴 했지만

실제로 쓸 때는 고정 폴더로 진행할 예정

그리고 dstDir는 서버에 업로드 할 거라서

매월 1일, 15일이 되면 자동으로 photoDir을 체크해서 새 파일이 있으면 업로드 할 예정이다.

윈도우는 작업스케줄러를 쓰라는데 웬지 별로 내키지가 않아서(?)

다른 방법 찾아봐야지.

#!/bin/python3
import sys
import os
from PIL import Image
import shutil
from PIL.ExifTags import TAGS
from datetime import datetime

# photoDir = 'p:\\photo'
# dstDir = 'p:\\Photo\\DSLR Photos'

class PhotoOrgarnize:
    def getImageData(self, photoDir):
        # if (datetime.now().day == 1) | (datetime.now().day == 15):      #매월 1일 15일에 photoDir에 새로운 파일이 있는지 체크
            photoPath = []
            photoInfo = {}
            for filename in os.listdir(photoDir):                       # photoDir의 파일들에 대해 반복
                if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
                    photoPath.append(os.path.join(photoDir, filename))  # 전체파일경로 만들기

            for filename in photoPath:
                image = Image.open(filename)                            #해당파일명의 이미지를 오픈
                exif_data = image.getexif()                             #이미지의 메타데이터를 저장해서 딕셔너리로 반환
                # print(exif_data)
                if (exif_data):                                         #메타데이터가 있다면 저장할꺼임
                    for tag, value in exif_data.items():                #tag번호와 value를 돌면서 확인
                        tag_name = TAGS.get(tag, tag)                   #태그번호에 대한 이름이 없으면 그냥 태그번호랑 똑같이 그대로 사용한다.
                        if (tag_name == 'DateTime'):
                            dateTimeTaken = value.replace(':', '').replace(' ', '')   #촬영날짜,시간부분을 : 공백 삭제처리하여 저장
                            photoInfo[dateTimeTaken] = os.path.basename(filename)       #파일경로부분 빼고 순수파일명만 따와서 value로 저장
            photoList = sorted(list(photoInfo.items()))                                 #photoInfo딕셔너리를 List로 변환(오름차순 정렬)
            return photoList

    def moveImage(self, photoList, photoDir, dstDir, fileId=1):
        prevDate = ''
        for dateTaken, filename in photoList:                #촬영날짜,시간을 dateTaken에 저장
            getDate = dateTaken[:6]                          #YYYYMM 형식으로 getDate에 저장
            dstPath = os.path.join(dstDir, getDate)        #목적지경로에 날짜이름으로 폴더경로를 생성.
            print(dstPath)
            if not os.path.exists(dstPath):
                os.makedirs(dstPath)                       #해당경로에 동일한 이름의 폴더가 없을 경우에만 폴더생성

            dateTaken = dateTaken[:12]
            if (prevDate == dateTaken):               #DateTaken이 동일할경우 fileId + 1 해서 새파일명을 구성한다.
                fileId += 1
            else: fileId = 1

            newFileName = '{}_{:04d}.jpg'.format(dateTaken, fileId)     #filename형식정하기 '날짜시분_0001'. 확장자는 모두 jpg로 통일
            prevDate = dateTaken

            srcPath = os.path.join(photoDir, filename)
            newDstPath = os.path.join(dstPath, newFileName)
            shutil.move(srcPath, newDstPath)
            print(f'{filename} moved to {newDstPath}')

            # fileName = String.format("%s\\sticker%04d.txt", Main.filePath, fileId);

#---------------------------------------------------------------------------------------------------------------------

orgarnize = PhotoOrgarnize()

if len(sys.argv) > 1:                               #argv[0]은 실행파일이름. 길이가 1개 이상이면(전달인수가 있으면)
    photoDir = sys.argv[1]                          #argv[1]에 photoDir로 저장
else:
    photoDir = input("원본 디렉토리를 입력하세요. ")
    if photoDir == '':
        exit(0)
if len(sys.argv) > 2:
    dstDir = sys.argv[2]
else:
    dstDir = input("이동시킬 디렉토리를 입력하세요. ")
    if dstDir == '':
        exit(0)

print(photoDir)
print(dstDir)
photoList = orgarnize.getImageData(photoDir)
orgarnize.moveImage(photoList, photoDir, dstDir)

 

 

다 하고보니 진짜 몇 줄 안되는 간단한 데다 별 어려운 내용도 없었는데 왜 이리 오래 걸린겁니까 휴먼?

토요일은 어쩔 수 없이 노는 날(?)이라 그렇다 치는데

(심지어 노트북 들고가서 데이트했음)

오늘은 아침 9시부터 매달려서 지금 끝났네..

흑흑 나의 이 초보딱지는 언제쯤 뗄 수 있는가🙄

 

만들 땐 메타데이터 추출하는 것도 찾아서 써 보고 나름 재밌게 만들었는데

다 하고보니 어.. 전체적으로 봤을 때 기능이 좀 재미가 없네 싶은게

결국엔 그냥 있는 파일들 경로만 바꿔서 옮겨놓는 수준이라

이전에 학원에서 예제로 진행했던 여러 함수들을 못 써본 게 조금 아쉽긴 하다.

뭐, 다른 거 또 아이디어 짜내서 만들어보면 되지!

다음엔 read write 기능을 좀 써 봐야겠다. 사실 요게 파일클래스의 꽃 아니겠능가.

(과연 그건 또 얼마나 걸리려나......)

 

 

처음엔 콘솔에서 일일이 디렉토리를 선택해서 들어가는 식으로 cmd스럽게 만들었으나

    def chooseDir(self, dirPath):
        while True:
            fileList = os.listdir(dirPath)
            for filename in fileList:
                if os.path.isdir(os.path.join(dirPath, filename)):
                    print(filename + "\t<DIR>")                 #파일경로 만들기. 디렉토리인 경우 DIR을 붙여준다.
                else: print(filename)

            choiceDir = input('해당폴더로 진행하시려면 1번, 계속 찾으시려면 폴더명을 입력하세요.')
            if (choiceDir != '1'):                              #계속 찾을려고 폴더명을 입력한다.
                new_dirPath = os.path.join(dirPath, choiceDir)  #입력한 폴더명을 저장
                if (os.path.isdir(new_dirPath)):                #해당 이름의 폴더가 존재하면
                    dirPath = new_dirPath                       #dirPath에 저장
                    continue
                else:
                    print('존재하지 않는 폴더입니다. 다시 입력해 주세요.')
                    continue

            print(dirPath)
            return dirPath

 

아빠가 뒤에서 구경하더니 비웃고 갔다(...)

 

 

아오 FileDialog만 쓰면 이런 귀찮은 일 안해도 되는데! (아니 그게 더 귀찮은가)

암튼 이 부실한 나의 코드를 조금이나마 있어보이게(?) 만들고 싶어서 msg를 조금 더 쳐 보기로 했다.

아빠가 CLI 이용하는 것도 이참에 연습 좀 해보는 게 어떻냐 했던 게 생각나 구글을 열심히 뒤졌다.

그래서 나온 게 sys모듈을 사용한 저 부분.

대단히 어려운 부분은 아니었어서 금방 바꿀 수 있었다. 

 

 

쨌든 잘 된다! 

자바 배울 땐 cmd 쓸 일이 별로 없었어서 잘 몰랐는데

요거 나름 재밌네. 옛날 어릴 때 생각도 나고🤣

이러면서 슬슬 리눅스를 시작하기 위한 밑밥이 되었달까.. 언젠간 하게 되겠지.

(커리큘럼에도 어차피 있어서;)

암튼 대충 이쯤에서 마무리....하고 싶으나

 

아 그래도 chooseDir 메소드 갖다버린 건 좀 아쉬운데

어떻게 좀 비벼서 끼워넣을 방법 없을까.🤔 고민중