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

!.. Tip

opencv에서 gif읽기 문제,한글 파일 문제, 디렉토리 걸러내기

rosehill 2024. 9. 1. 04:16

 

opencv에서 gif읽어들이기.. 

gif는 이미지 파일이면서 imghdr.what(imgpath)를 통해서도 결과값 'gif'를 출력해주는 만큼 문제는 없는데, opencv에서는 이 gif를 읽어들일때 문제를 일으키게 된다. 해서 이놈을 별로도 읽을 수 있는 처리를 하던가 솎아내는 작업을 해줘야 하는데, 다음의 경우처럼 읽어들이면 읽을 수 있다. 

소스참조 

# 일단 이 함수를 통해 gif파일 문제와, 한글 파일 문제는 해결된다. 

def loadImageFromPath(imgPath): 
  try:    
    #--- gif 파일만 여기서 따로 처리해준다.
    if str(imgPath).lower().endswith('.gif'):
      gif = cv2.VideoCapture(imgPath) 
      ret, frame = gif.read()  
      if ret: 
        return frame
    # ---------------------------------  
    # 나머지는 그냥 cv2.imread(imgpath)로 해도 되는데, 한글이름의 파일을 처리하려면 이렇게 해줘야한다.  
    readFile = cv2.imdecode(np.fromfile(imgPath,dtype=np.uint8),cv2.IMREAD_COLOR)
    return readFile
    # 파일을 uint8형태로 다시 읽어들이고 또 다시 decode를 하면서 array형태로 바꿔준다. 
    # 여기까지는 cv2.imread의 작업이 끝나는것과 같다. 
        
      
  except Exception as e:
    print("++++++++ exception error !! : "+e)
    return None

그리고 이때 리턴 되는 값은 곧 바로 cv2.imread(imgPath)와 같은 상태가 되어, 바로 cv2.imshow("img",img)를 통해서 이미지를 문제 없이 띄울 수 있게된다. 

하나씩 보면.. 

if str(imgPath).lower().endswith('.gif'):
	gif = cv2.VideoCapture(imgPath) # videoCapture로 받으면서
    ret, frame = gif.read()  # read한다 
    if ret: # ret가 true가 되면 모두 다 읽은 것이므로 이 값 frame을 리턴한다.    
    return frame

if문에서, 지금 넘어온 전체 경로(imgPath)중에서 endswith('.gif')를 통해서 지금 넘어온 이 파일이 지정된 접미사로 끝나면 참(True)을 리턴하게되는데, 그말은 곧 이 파일이 gif파일임을 의미하는것이니 이 조건이 맞으면 이때 이 파일은 여기서 따로 처리하겠단 얘기다.

특이한것은 얘는 cv2.imread로 읽어들이는것이 아니라, videocapture()라는 클래스를 통해 읽어들인다. opencv에서 gif는 여러겹의 프레임으로 이뤄진 동영상으로 취급하는것같다. (이로 인해 imread나 imshow에서 에러발생.. )

그렇게 해서 하나의 프레임을 가져오고 이 프레임부터 시작해 gif.read()를 통해서 읽어들이는데, 이때 두개의 값이 리턴된다 하나는 ret로 하나는 frame으로 값을 받는데, 전자에는 read가 끝나고 난 후 true가 리턴되며 이 값을 받고, 후자는 그때까지의 모든 프레임을 읽어들인다. 그리고 이 frame을 리턴하게 되는데, 이 상태는 곧 cv2.imread(imgPath)의 상태와 동일한 상태가 된다. 

한글 파일 인식 문제

그리고 나머지 부분은, 그냥 문제 없이 

img = cv2.imread(imgPath)
return img

 해도 된다. 그런데 여기서 한글파일의 인식 문제를 끼워넣을 수 있다. 

한글 파일을 읽어 들일때 종전의 위의 방식대로 하면 에러를 발생시키게 된다. opencv의 몇몇 군데에서 이 문제가 좀 있다. 그래서 웬만하면 영문이름위주로 작업하는게 나을것같다. 그런데 기왕 잡을 수 있다면 잡는게 좋으니, 이 부분은 다음처럼 수정한다. 

readFile = cv2.imdecode(np.fromfile(imgPath,dtype=np.uint8),cv2.IMREAD_COLOR)
    return readFile

fromfile을 통해서  uint8이 적용된형태로 읽었다가 다시 이것을 decode를 해주므로서 한글이 들어간 파일명을 무리 없이 읽을 수 있게 된다. 

이렇게 이 함수 안에서 gif문제와 한글 파일 문제가 모두 해결이 된다. 

여기서 리턴 되는 값을 통해서.. 바로 imshow작업을 하면 이미지가 gif가됐던, 한글이름이 됐던 에러 없이 띄우게 된다. 

img = loadImageFromPath(filePath) # 이미지 처리 함수로 전송 디코딩 된값을 리턴받는다. 
cv2.imshow("this is [ "+imghdr.what(filePath)+" ]",img)

리턴된 값을 img 변수로 받고 이 변수를 통해 imshow를 실행한다. 

imghdr.what(filePath)부분은 캡션 제목에 현재 파일의 확장자를 보여주기 위해 넣어본것이다. 

imghdr.what(filePath)의 결과는 캡션제목부분에 저렇게 나타나게된다.물론 이미지 파일이 아닌경우엔, None으로 출력된다 물론 image파일이 아니니 이미 그전에 지금 이 원리를 통해 미리 걸러낼 수 있을것이다.

한글이름의 파일을 출력하는데는 이제 문제가 없어졌지만, 캡션 제목같은곳에 파일명을 출력하게 하면..이를테면, 

cv2.imshow("this is "+fileName,img)
# 캡션 제목에 현재의 파일명이 들어가게 하는경우.. 
# 여전히 한글파일은 이 안에서는 깨져서 나타나게 된다.

이미지도 출력되고, 콘솔상에서 한글로 된 파일명도 잘 출력되지만 여전히 캡션제목에선 저렇게 깨져 나오게 된다. 이문제는 좀더 생각해 봐야 할 것같다.

디렉토리 걸러내기

사실은 이 문제 부터 해결하고 들어가야 하는게 맞다. 현재 pixagoras같은 경우는 하나의 폴더에 그림파일을 몰아넣고 이 폴더를 통해서 랜덤하게 이미지를 불러오면서 다양한 효과가 부여되는것을 통해 감상하는 것인데, 그림파일만 딱딱 골라서 넣으면 좋지만 대개 실수하거나 혹은 번거로울 수 있기때문에, 그냥 폴더안에 아무파일이나 막 들어있다는 가정하에, 이 모든것을 자체적으로 프로그램이 분류하면서 오직 읽을 수 있는 그림파일만 알아서 골라내어 읽어들이게 하는것이 좋을것이다. 

그래서 일단 

1. 이미지만 찾아서 솎아내고,

2. 디렉토리는 애초부터 담지 않아 버리고,

3. 이렇게 해서 이 저장된 리스트를 통해서 이미지 효과를 부여해주면 되는것이다. 이제 이 안에는 공식적으로 읽을 수 있는 이미지만 존재하게 하는것인데, 이 작업은 imghdr.what()을 통해서 분류하면된다. 

4. 그런데, 지금 했던 작업은 이렇게 문제없는 이미지 파일인데도, 한글이름의 파일이나 혹은  문제 없는 정상적 이미지 파일인 gif의 경우는 저 과정에서 걸러 주기 어렵기 때문에 위의 작업을 통해서 따로 처리를 해 준것이다. 

그런데, 사실 그전에 1,2번의 작업이 먼저 이뤄지는게 편할것이다. list에 담을때 부터 일단 먹을 수 있는것부터 담고 그 안에서 세심하게 골라내는게 나을테니 말이다.

#디렉토리 걸러내기.. 

list_dir = ['a.jpg','b.gif','c.png',folder] # 이렇게 담겨있다고 가정하고..
# 여기서 folder라고 써 있는 놈은 딱 봐도 그림파일이 아닌 디렉토리임을 알 수 있다.

list_up =  [] # 솎아내면서 새로 담을 list_up이란 빈 변수를 만든다 

for item in list_dir: # 위의 list_dir를 뒤적이며 하나씩 꺼내면서 디렉토리는 걸러낸다.       
    fullPath = path_dir+item # 우선 절대경로를 잡아주고 
    if os.path.isfile(fullPath): #해당되는 놈인경우만 담는다. 
      print("this is file")
      list_up.append(item) # isfile(fullPath) 경로에 위치한 파일이 파일이라면 (디렉토리면 false가 된다.)
      # 아까 빈 배열에 덧붙여 넣는다.
# 모든 작업이 다 완료 되면, 즉 for문이 끝나면
list_dir = list_up # 새로 걸러 내서 담은 데이터를 원래 데이터가 담겨있던 list_dir에 할당하면서 새로 값을 초기화 한다.

위의 예제에서는 list_dir = ['a.jpg'...]식으로 간단하게 몇개 담아놓았지만,실제는 os.listdir(path)를 통해서 해당 디렉토리의 모든 파일을 다 저장하고 담아놓게 되어있다. 이렇게 담아놓은것들 중에서 imghdr.what()을 통해서 유효한 이미지를 걸러내고, 또 디렉토리등도 걸러내야 한다. 그러고 나서 유효한 그림파일 즉, imghdr.what()에서 공식적으로 이미지로서 인정하는 모든것들만 담기게 하고, 디렉토리를 걸러낸 이후에 이제 이 리스트안에서 세부적으로 gif문제, 또 한글명 문제를 해결해야한다.

imghdr.what()의 경우는 지원되는 경우는 해당 이미지 파일의 확장자를 리턴하게 되어있고, 해당되지 않는 것들이나 혹은 그림파일이 아닌경우는 None을 리턴하게 된다. 이걸 통해서 위의 경우처럼 update를 하며 채우면되나 여기서는 그 부분은 생략하였다. 

디렉토리의 경우는 isfile()을통해서 확인하는데, 디렉토리인경우 false를 리턴하게 된다. 

그러니 이게 참이면 일단 디렉토리는 아니기때문에 이제 이놈들만 따로 담고 이 따로 담은 놈을 원래의 list_dir에 할당해주면 이제 유효한 이미지 파일들만 남게 된다. 

그리고 이 작업이 완료 된 이후에 이제 저 위의 gif문제나 한글파일명등을 잡아주는 구현을 하면 이제 원하는 정도까지 진행이 될것이다.