"참된 지혜는 실용적인 지식들의 무분별한 집적을 통해서 얻어지는것이 아니라, 모든것들을 통해서 자신을 드러내는 하나의 것을 파악하는데 있다. " - 헤라클레이토스 -

!.. Tip

[opencv] imread,imwrite에서 한글 파일명과 gif문제 관련 보완.

rosehill 2024. 9. 14. 19:26

먼젓글에서 

 

[python] opencv에서 한글명 파일의 저장과 gif저장문제

한글명파일이나,gif이미지 파일을 읽어들여서 보여주기 까지는 해결이 되었는데, 저장하는 부분은 처리가 되지 않았다. 한글부분은 처리가 될 수 있겠는데, opencv에서 gif와 별로 사이가 좋지 않

rosehill.tistory.com

먼젓글 (윗 링크) gif파일이나 한글 파일명등을 opencv의 cv2.imread나 cv2.imwrite에서 미흡한 점이 있기때문에 따로 함수를 만들어 쓰는것을 구현했었다. 소스를 들여다 보다가, 다소 복잡한 형태로 마무리 된듯하여 추가적으로 조금 수정하여 imread함수와 imwrite함수를 수정보완해서 가져다 쓰기 쉽게 바꿔 보았다. 혹시 다양한 용도로 사용할때 참고하거나 혹은 가져다 써도 될것같다. 


'''# 보통 한줄 주석은 #으로 시작하는데, 여기서는 글이 잘 안보이는듯하여 홑따옴표 방식을썼다'''
'''# 먼저 opencv에서 일반적으로 cv2.imread로읽어들이는데 문제가 있는경우와,'''
'''# 역시나 그렇게 읽어들인 후 cv2.imwrite로 저장을 하는 과정에서 역시 동일한 문제가 발생되기때문에.. '''
'''#이런경우 그 부분을 대체할만한 새로운 imread,imwrite를 만들어서 사용할 수 있게 함수로 구현 '''
'''#cv2.imwrite나 imread대신에 사용하면 될것같다.  '''

import numpy as np
import cv2 
import os
import imghdr 


fileName = 'sample.gif' # 이미지 파일명.. 
pathDir = os.getcwd() #현재 내가 위치한 폴더.

''' # 읽기 관련. (읽을 폴더 위치, 읽을 파일이름까지 포함한경로 두개를 정의) '''
readPathDir = pathDir+'\\image\\'
readFilePath = readPathDir+fileName

''' # 쓰기 관련. (저장할 폴더 위치, 저장할 파일명까지 포함한 경로 두개를 정의) '''
savePathDir = pathDir+'\\image\\saveImg\\' 
saveFilePath = savePathDir+fileName


''' # 읽기 함수.. cv2.imread(img,flag)를 대체할.. '''
def imread(_filePath, _flags=cv2.IMREAD_COLOR, _dtype=np.uint8):
    try:
        n = np.fromfile(_filePath, _dtype) 
        decodedImg = cv2.imdecode(n, _flags) 
        if(str(_filePath).lower().endswith('.gif')): '''# 얘가 gif라면.'''
          gif = cv2.VideoCapture(_filePath) '''# 이 방식으로 캡춰하여 image를 읽어라.'''
          ret,frame = gif.read()
          if ret:
            return frame '''# 읽기가 끝나면 이 frame을 리턴하고'''
        else:
          return decodedImg '''# gif가 아닌경우는 위에서 decoding작업된 그 상태 그냥 리턴하라. 이부분은 한글 파일명 문제 해결.''' 
    except Exception as e:
        print(e)
        return None   


img = imread(readFilePath,cv2.IMREAD_COLOR,np.uint8) '''# cv2.imread() 수정 보완된 기능 수행.
''' # 읽어들인 후의 opencv가 가지고 작업할 image의 data '''
''' # img는 곧,아직은 저장되지 않은 메모리 상에 로딩된 데이터.. '''



'''# 쓰기 함수.. cv2.imwrite('filename',img)를 대체할.. ''' 

''' #이제 이 img를 저장하는데, 마찬가지로 한글파일명이라거나, gif같은경우를 대비해서'''
''' cv2.imwrite대신에 이 함수를 이용한다. '''

def imwrite(_saveFilePath,_img,_params = None):
  try:    
    ext = os.path.splitext(_saveFilePath)[1]     
    if (str(_saveFilePath).lower().endswith('.gif')): ''' # gif인경우만 날라오는 매개변수 몇개를 바꿔준다.'''
      ext='.jpg' ''' # 우선 확장자는 내가 jpg로 만들기로 했기때문에 고정값을 할당해주고'''
      paramsForGif = [cv2.IMWRITE_JPEG_QUALITY,100] '''# flag값은 원래 None으로 보낼거지만, 이 경우는 jpg용 flag값을 할당해준다.'''
      _params = paramsForGif  '''# 이걸 _params (지금은 None인..)에 할당해준다. '''
      '''# 이 부분은 가독성을 고려해 두번에 걸쳐서 했다. '''
    
    ''' #이하 나머지 부분은 동일하기에 그냥 합쳤다. '''
    result,n = cv2.imencode(ext,_img,_params) '''# encoding을 통해 opencv가 작업한 형태에서 곧 저장할 능력이 되는 numpy형으로 encoding을 해주고있고'''
    if result: 
      with open(_saveFilePath,mode = 'w+b') as f: 
        n.tofile(f)      '''# 여기서 저장이 이뤄진다.''' 
        print("save success")  
        return True
    else:
      return False
  except Exception as e:
    print(e)
    return False


imwrite(saveFilePath,img,None)  '''# 함수는 이렇게 호출 flag값은 기본 None으로 보낸다.''' 

''' 이 부분은 저장된 gif가 실제로 어떤 타입인지 확인 하기 위해서 ''' 
''' 코드와 상관없기에 그냥 참고용으로.. 주석처리함. '''
'''print("saveFIlePath ; ",saveFilePath)'''
'''print("imghdr.what(saveFilePath) : jpg? gif? ====> ",imghdr.what(saveFilePath)) #imghdr에서는 jpeg로 나온다.'''

따로 부연하지 않아도 될것같고,

 cv2.imread(imgPath,flag)대신에 사용되어질 read함수.. 파일로 부터 읽어들일때 numpy의 신세를 지고, 이렇게 읽어들인후 이놈을 디코딩하여 opencv방식으로 바꿔서 opencv가 작업을 하게 하는게 목적.. 이 함수를 실행하면 두가지의 리턴값이 나오게 되는데, gif인경우는 frame이란게 리턴되고, 그냥 일반 이미지는 decodedImg가 리턴된다. 하나는 한글파일명같은 문제가 혹시 생길것에 대비해 decoding과정을 거쳐서 리턴이되는데, 이걸 한 이후에도 읽지 못하는 gif같은 경우는 여기서 따로 read작업을 더 해준다. VideoCapture라는 방식으로 .. 얘는 이과정이 끝난것이 리턴이되고, 나머지는 그냥 1차 decoding상태로 리턴된다. 

파일을 최초로 여는 놈은 np.fromfile이란놈이다 이놈은 numpy 모듈로 이 모듈로 한글파일명은 무리없이 열수있다. 이 렇게 오픈한 파일을 이제 decoding작업을 통해 opencv는 자기가 채가는 것이다.

opencv는 이 리턴되서 받은 놈을 가지고 작업을 하게된다. 해당부분에서 img는 opencv가 계속적으로 작업할 data가된다.

img = imread(readFilePath,cv2.IMREAD_COLOR,np.uint8) '''# cv2.imread()의 수정 보완된 기능 수행.'''

이제 신경쓸것없이 작업을 하다가 최종적으로 저장할때 다시 이제 저장할줄 아는 놈 말하자면, 원래 파일명 그대로 저장한다고 할경우에 처음 한글파일명이 이때 저장시에도 문제가 되기때문에 이 부분과 관련해서.. 또, gif파일 역시도 저장시엔 문제가 또 되기때문에 opencv에서는 저장시에도 이를 처리할 대리자를 잠깐 불러서 저장을 하게 하는데, 이 부분이 바로 imwrite함수 부이다.. 이땐 읽을때와 다르게 지금 opencv의 형태로 되어있는 데이터를 저장하는 놈에게 맞게 바꿔줘야 하는데 이것이 encoding작업이다.

지금까지 opencv가 작업했던 img를 저장할줄 아는 놈( 역시 numpy)에게 넘겨주기 위한 작업이 이뤄지는데 이것이 encoding작업이다 실은 numpy배열 형태로 바꿔주는 것이다. 그럼 이제 얘가 n이라는 값으로 받고 하단부에서 n.toFile(f)라는 것을 통해 저장(즉, 실체화 하드에 저장되는 일종의 굳히기)를 하게 된다.

앞서도 정리했지만, write의 매개변수 세개는 첫번째가 저장이 될 경로와 이때 저장이 될 파일의 이름.. 일일이 인위적으로 설정하지 않고, 원래 파일 이름 그대로 저장이 될것이다. 이경로를 담고있고, 두번째는 지금까지 opencv가 작업했던 아직 저장되지 않은 data즉, read를 받았던 img가 되며, 세번째는 인코딩을 하게 될텐데, 여기서 세번째 인자가 인코딩을 하는 파일의 확장자와 관련된 flag어떤 옵션이 들어가는데, 기본 None값은 일반적으로 해당되는 이미지의 형태에 맞게 디폴트 값이 들어가게된다. 그런데 내 경우는 gif를 인위적으로 jpg로 바꿔서 1.일단 에러가 발생되지않고,  2.저장도 되며, 3. 어쨌건 이미지를 볼 수 있는 ...상태로 만들거기때문에 이때는 flag값을 가급적 주기로 한다. 

그래서 동일한 값이 전달되서 적용이 되며 작업이 이뤄지는데, 이때 gif파일만 살짝 채 서 얘만 매개변수값을 새로 설정해 주면 된다. 

나머지는 동일한 과정이기에 상관이 없다. 이전 글에서 이부분이 미흡해서 하나로 통합하고 간소화 시켰다. 

맨 하단부는 , 이렇게 jpg로 저장이 되었는데, 실제 저장은 원래 파일명인 sample.gif로 저장이 된다. 그런데 얘가 정말 gif가 맞는지 확인 해 보기 위함이다.. 당연히 imghdr.what(해당이미지)는 jpeg로 나옴을 알 수 있다.