ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Python/사진, 동영상 정리 V2
    코딩/Python 2024. 1. 23. 22:24
    728x90

    이전 프로그램에 문제가 있었다.

    날짜와 시간으로 사진 이름 변경 - 0. 개요

     

    Python/날짜와 시간으로 사진 이름 변경 - 0. 개요

    스마트폰 덕분에 사진이 매우 많은데 관리를 하는데는 어려움이 있다. 동영상은 끊어서 저장하는 경우도 많아 파일 이름으로 분류하기가 쉽지 않다. 또한 스마트폰 뿐만 아니라 카메라, 액션캠

    summertrees.tistory.com

    사진 확인 없이 CSV로만 데이터를 변경하는 게 힘들었다. 그리고 파일이름을 시간 순서대로 변경해야 하는데 파일이름 변경하는 건 CSV에서 고려가 없었다.

    파일이름은 어쨌든 사진을 보면서 순서대로 바꿔야 하니 수작업일 수밖에 없었다.

    그렇게 파일이름을 바꾸고 난 뒤, 파일이름을 따라 사진은 Exif, 동영상은 생성일을 바꿔주는 방식으로 프로그램을 바꿨다.

    어쩔 수 없이 command line tool인 exiftool, setfile을 썼는데, 기능은 만족스럽지만 처리속도가 느린 게 단점이다.

    DATA

    입력해야 하는 데이터는 여기서 모두 세팅한다.

    subprocess와 call이 받을 명령어는 lamba식을 사용한다. 추후 받을 인자를 미리 세팅해 두기 위함이다. 그리고 subprocess는 데이터를 받을 때, call은 데이터를 받을 필요가 없을 때 사용한다.

    람다식(Lambda Expression)과 지연 함수

     

    [Python] 람다식(Lambda Expression)과 지연 함수

    람다식(Lambda Expression)과 지연 함수 함수형 프로그래밍에서 중요한 개념인 람다식(Lambda Expression)에 대해 알아보고, 그 중에서도 지연 함수의 개념과 활용 방법에 대해 알아보겠습니다. 람다식은

    summertrees.tistory.com

    DATA = {
        'path':'/your/path',
        'extensions_img':['.jpg', '.jpeg', '.heic', '.png'],
        'extensions_mv':['.mp4', '.mov'],
        'format_filename':'%y%m%d-%H%M%S',
        'format_exif_time':'%Y:%m:%d %H:%M:%S',
        'format_time_setfile':'%m/%d/%Y %H:%M:%S',
        'cmd_get_exif_time': lambda fp: ['exiftool', '-DateTimeOriginal', fp],
        'cmd_set_exif_time': lambda t, fp: ['exiftool', f'-DateTimeOriginal="{t}"', fp],
        'cmd_setfile': lambda t, fp: f'SetFile -d "{t}" -m "{t}" "{fp}"'
    }
    728x90

    main()

    main()에서 필요한 데이터를 세팅하고 서브 프로그램을 호출한다.

    def main():
        path = DATA['path']
        extensions_img = DATA['extensions_img']
        extension_mv = DATA['extensions_mv']
        format_filename = DATA['format_filename']
        format_exif_time = DATA['format_exif_time']
        format_time_setfile = DATA['format_time_setfile']
        cmd_get_exif_time = DATA['cmd_get_exif_time']
        cmd_set_exif_time = DATA['cmd_set_exif_time']
        cmd_setfile = DATA['cmd_setfile']
    
        # get file list
        fs_img, fs_mv = get_files(path, extensions_img, extension_mv)
        
        # modify image time
        fs_img_modified = modify_time_exif(path, fs_img, format_filename, format_exif_time, cmd_get_exif_time, cmd_set_exif_time)
        
        # modify movie time
        fs_mv_modified = modify_time_birth(path, fs_mv, format_filename, format_time_setfile, cmd_setfile)
        
        print(f'Images modified: {fs_img_modified}')
        print(f'Movies modified: {fs_mv_modified}')

    폴더의 파일 리스트를 생성하는 get_files()

    항상 쓰는 코드. 폴더 내의 파일을 리스트로 만든다. 확장자를 기준으로 디렉토리는 제거하고 이미지 파일과 동영상 파일로 구분하는 리스트를 반환한다.

    def get_files(path: str, extensions_img: list[str], extensions_mv: list[str]) -> tuple[list[str], list[str]]:
        '''
        Returns file list (files_image, files_movie)
        '''
        fs = os.listdir(path)
        fs_img = []
        fs_mv = []
        for f in fs:
            _, e = os.path.splitext(f)
            if e.lower() in extensions_img:
                fs_img.append(f)
            elif e.lower() in extensions_mv:
                fs_mv.append(f)
        fs_img.sort()
        fs_mv.sort()
        return fs_img, fs_mv

    이미지 파일의 Exif data를 수정하는 modify_time_exif()

    이미지 파일(.jpg, .jpeg, .png, .heic)은 Exif를 지원하므로 Exif 데이터를 수정해서 날짜를 바꾼다.

    .heic 외에는 이런 고생을 할 필요 없는데, .heic의 exif를 수정하려면 command line utility인 exiftool을 사용하는 방법이 유일하다.(내가 아는 한) 그래서 exiftool을 사용해서 exif date를 수정한다.

    exiftool은 command line utility이므로 subprocess 라이브러리를 이용해서 수정한다. 단, command line utility로부터 받은 데이터는 형식을 지정하지 못하고 받은 값을 변형해야 하므로 string 데이터의 변형자를 이용해 필요한 데이터로 가공해야 한다.

    또한 적절한 답을 받지 못할 수도 있으니 에러대책도 역시 코드에 포함시켜야 한다.

    파일 이름의 날짜와 Exif 데이터의 날짜를 비교해서 값이 틀리면 파일 이름 날짜로 Exif 데이터를 수정한다. 이 때는 timedate 라이브러리의 time값으로 비교한다.

    .heic 파일이 아닌 .jpg, .jpeg의 exif값을 수정하려면 이런 방법이 불필요하다(매우 매우 쉽다. 고민할 필요가 없다. Python의 거의 모든 라이브러리에서 수정 가능하다. 호환성이 왜 중요한지 알 수 있음). .heci가 비표준이고 저작권문제가 있어 Python의 image 관련 라이브러리가 .heic 메타데이터 수정코드를 넣지 못하고 있다. 그래서 애플폰을 사용하는 사용자들 외에는 이런 문제가 발생하지 않는다. 다만, .heic의 파일크기가 .jpeg의 1/2 또는 1/3인 걸 감안한다면(수치는 주관적) .heic로 사진들을 보관하기를 추천한다.(사진의 품질은 논외로 한다. 애플이 카메라의 기본 파일 형식을 .heic로 했기 때문에 내가 논할 일은 아니다) .hiec의 파일크기가 확실히 작기 때문에 .jpeg로 파일변환은 고려하지 않는다. (이미 애플이 결정한 순간 내 사진은 모두 .heic로 저장됐다. 그걸 .jpeg로 변환한 들 무슨 소용? ㅠㅠ)

    def modify_time_exif(path: str, files: list[str], format_filename: str, format_exif_time: str, \
        cmd_get_exif_time: list[str], cmd_set_exif_time: list[str]) -> list[str]:
        
        # modify date of images with exiftool
        fs_modified = []
        for f in files:
            # change filename to time
            print(f'Processing {f}')
            fname, _ = os.path.splitext(f)
            time_fname = dt.datetime.strptime(fname, format_filename)
    
            fp = os.path.join(path, f)
            try:
                # ExifTool command: exiftool -DateTimeOriginal file/path
                cmd = cmd_get_exif_time(fp) # type: ignore
                result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
                output = result.stdout
                if output == '':
                    time_exif = output
                else:
                    time_string = output.partition(':')[2].strip()
                    time_exif = dt.datetime.strptime(time_string, format_exif_time)
                if not time_fname == time_exif:
                    # ExifTool command: exiftool -DateTimeOriginal='20240101-102400' file/path
                    time_string = dt.datetime.strftime(time_fname, format_exif_time)
                    cmd = cmd_set_exif_time(time_string, fp) # type: ignore
                    subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
                    fs_modified.append(f)
            except subprocess.CalledProcessError as e:
                print(f'Error: {e}')
                print(e.stderr)
        return fs_modified

    SetFile로 파일 생성일을 변경하는 modify_time_birth()

    동영상 파일은 Exif를 지원하지 않는다. 그래서 실제 파일의 생성일을 수정해야 한다.

    하지만 POSIX(Mac OS)에서는 정상적인 방법으로는 파일 생성일을 수정하지 못한다. 그래서 특별한 툴, SetFile을 사용해야 한다.

    Python/Mac/POSIX/SetFile로 파일 생성일 변경

     

    Python/Mac/POSIX/SetFile로 파일 생성일 변경

    GoPro Max 등의 360 카메라는 영상을 촬영하고 후처리를 통해 MP4 영상을 추출한다. 이렇게 만들어진 MP4 동영상은 촬영일자가 실제 영상을 촬영한 날짜와 달라 파일관리와 영상편집등에서 사용하기

    summertrees.tistory.com

    사용법은 매우 쉬워 subprocess의 call()을 이용하면 간단하게 처리된다. 결과값을 따로 처리하지 않으니 더 쉽다.

    def modify_time_birth(path: str, files: list[str], format_filename: str, format_time: str, call_command: str) -> list[str]:
        '''
        SetFile로 파일생성일 변경하고 변경된 파일 목록 반환.
        '''
        fs_modified = []
        for f in files:
            print(f'Processing {f}')
            fp = os.path.join(path, f)
    
            # time from file name
            filename, _ = os.path.splitext(f)
            time_fname = dt.datetime.strptime(filename, format_filename)
            
            # time from file
            time_stamp = os.stat(fp).st_birthtime
            time_file = dt.datetime.fromtimestamp(time_stamp)
            
            if not time_fname == time_file:
                time_string = dt.datetime.strftime(time_fname, format_time)
                cmd = call_command(time_string, fp) # type: ignore
                subprocess.call(cmd, shell=True)
                fs_modified.append(f)

    결론

    이렇게 함으로써 파일의 관리가 쉬워졌다. 이제 GPS값을 정리하는 것이 남았다. 이미 GPS값을 처리하는 코딩은 있는데 이 프로그램과는 호환이 되지 않아 다시 작성해야 한다.

    그리고, 파일의 이름과 날짜를 변경하는 프로그램도 필요한 듯 한데 그리 실용성은 높지 않은 것 같아 이 프로그램으로 미디어파일 관리를 하려고 한다.

    그런데 맥과 아이폰은 특별한 구석이 있어 업무생산성은 영 좋지 않다. 역시 생산성은 MS. 내가 맨날 Mac을 쓰지만 업무용으로는 절대 비추. 외국 영화에 Mac 쓰는 건 구라다. 일 정말 못하는 인간들이나 맥 쓰는 거라고 한탄하며 본다.. 하물며 Office 프로그램도 윈도우랑 다르게 불편하다. 윈도우에선 이런 고생하지 않는다.

    728x90

    댓글

Designed by Tistory.