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

!.. Tip

파이썬 opencv에서 파비콘(favicon) 관련..

rosehill 2024. 8. 23. 15:58

픽사고라스(pixagoras)를 갑작스레 만들어 가는 과정에서...

보통 파이썬에서는 트킨터를 통해서 창을 만들고하는 그런 과정을 거치지 않고, 일단 구현해 보려는 목적에 맞게 구성을 하다 보니까, 내경우는 opencv상에서 cv2.imread와 cv2.imshow를 통해 읽었다가 보여주는 식으로 되어있다. 지금까지는 그렇게 되어있는데, favicon때문에.. tkinter와 느슨하게 조합된 상태..

별로 문제는 되지 않았었는데, 얼마전에 (이번에 업 해본) exe로 만들고 몇가지 수정을 하는 과정에서 보니까, 파비콘을 넣을 방법이 궁금해졌다. 생각해보니까 현재 보는 모드가 tkinter가 아니라 opencv의 imshow로 보고있는게 아닌가.. 

icon의 경우는 exe를 만들때, spec파일을 수정해주면서 경로를 잡아주면되는데 얘는 그렇게 간단하지가 않은것같다.

파비콘(favicon).. 창이 떴을때 나타나는 저런 아이콘 (노란색 사각형 안)...

일단 검색을 해 봤다. 

결국 stackoverflow에서 어슴프레 답을 찾았는데. . (국내 검색에선 잘 나오지 않아서..) 

// cvGetWindowHandle is CHILD window in opencv_455
static HWND _GetCvWindow(LPCSTR lpWndName) {
    HWND hWnd = (HWND)cvGetWindowHandle(lpWndName);
    if (IsWindow(hWnd)) {
        HWND hParent = GetParent(hWnd);
        DWORD dwPid = 0;
        GetWindowThreadProcessId(hWnd, &dwPid);
        if (dwPid == GetCurrentProcessId()) {
            return hParent;
        }
    }

    return hWnd;
}

// param 1: window name, first parameter of imshow
// param 2: resource id
static void _SetCvWindowIcon(LPCSTR lpWndName, WORD wIconId) {
    HWND hWnd = _GetCvWindow(lpWndName);
    if (IsWindow(hWnd)) {
        HICON hIcon = LoadIconW(gs_hInstance, MAKEINTRESOURCEW(wIconId));
        SendMessageW(hWnd, (WPARAM)WM_SETICON, ICON_BIG, (LPARAM)hIcon);
        SendMessageW(hWnd, (WPARAM)WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
    }
}

얘를 이용해서 하라고 하는데, 정확하게는 이해 못해도 대충은 이해 할 수 있겠다싶다. c++ API쪽 코드인데 대강 보니까 위엣놈은 HWND 핸들값을 얻어오고, 이 얻어온걸 통해 밑엣놈에게 전달해서 콜백으로 셋팅하게는것같다. 막상 이해는 하지만 어떻게 구현하느냐.. 한창 생각하고 있는데, 밑에 답글단 사람이 정답을 올려놓았다. 

# 이거는 파이썬으로 구현된것.

import cv2
import numpy as np
import win32gui,win32con

#Just a background example using Numpy module.
bg_example = np.full((512,512,3),(0,0,0),np.uint8)

#Showing the GUI of OPENCV
cv2.imshow("MyWindow", bg_example)

hwnd = win32gui.FindWindow(None, "MyWindow")
icon_path = "my_icon.ico" #You need an external .ico file, in this case, next to .py file
win32gui.SendMessage(hwnd, win32con.WM_SETICON, win32con.ICON_BIG, win32gui.LoadImage(None, icon_path, win32con.IMAGE_ICON, 0, 0, win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE))

#ESC for exit
cv2.waitKey(0)

win32gui, win32con을 이용하는것같다. 확실히 웬만 하면 모듈화가 되어있어 심플하게 되어있다. 

앞에서는 example이란 사진 파일을 하나 만들면서 512사이즈에 R,G,B(여기는 B,G,R이겠구나..)로 0,0,0칼라 즉 검은색이겠지.. 이걸로 채우면서 임시로 윈도우 창을 하나 만들고, 이놈에 이름을 붙인담에 이 이름에 해당되는 놈의 핸들값을 얻어오는데, (저 위의 C++의 HWND가 그걸 담듯이..  내부적으로는 저 위에서 나오는 과정을 통해 얻어올게다..)

역시 파이썬은 모듈화가 잘 되어있어서.. 복잡하지 않게, 얘는 그냥 간단하게 win32.gui클래스내의 FindWindow()를 통해서,지금 막 만들어준 이름인 "MyWindow"를 들이대면, 알아서 핸들값을 잡아서 현재 None이라는 곳에 부여해주면서 이 주소값을 리턴하게 되어있는것같다.. 뭐 좌우간 핸들값을 얻어다가 hwnd에 넣어준다는 의미.. 

이때 이 값을 받아둔 상태에서.. favicon을 배치 해야 되는데, 뭔지 자세히는 모르겠지만,

win32gui.SendMessage(hwnd...생략.. , win32gui.LoadImage(None,icon_path~~~~))

를 통해서 전달을 하는데, 이 안에 매개변수(매개함수)로 또 로딩해서 보여줘야 될 icon에 대한 정보를 담은것으로 여겨지는..  .. win32gui.LoadImage()  함수가 들어가서 우리가 넣고자 하는 icon(즉, 파비콘 작은 그림..)에 대한 정보를 함게 보내면서 셋팅이 이뤄지게 되어있다. 복잡해 보이나 군더더기 걷어내면 그런 원리인듯하다. 

좌우간 저기서 몇가지를 수정해서 favicon을 넣었는데, 얘는 spec에서 설정할 수 없어 그런지, exe만들고 나서 해당되는 icon을 넣어줘야 했다. 

이걸 하면서 느낀거는 픽사고라스(pixagoras)가 지금은 그냥 이렇게가는데 tkinter에 이걸 담던가 해서, 이 tkinter를 웹상에서 바로 보여줄 수 있게 하는 방식도 향후에 해 보면 어떨까 생각해본다. 사실 이런거를 exe로 가는건 좀 아니다 싶다. 이건 하나의 작품처럼 어떤 페이지에 만들어놓고 어떤 유저가 들어와 봤을때 볼때마다 유니크한 오직 지금 보고 있는 자신만 처음이자 마지막으로 보는 작품이 되는 그런 형태를 생각해 본다. 이것은 오직 프로그램만이 가능한것이 아닐까..