OpenCV GUI
OpenCV는 이미지/영상처리의 대표격인 라이브러리 입니다.
이미지/영상 관련한 일은 뭐든지 할 수 있을것 같습니다.
하지만, GUI만큼은 지원하지 않고 있는데, 이 부분이 아쉬워 직접 만들어 봤습니다.
OpenCV를 이용한 GUI
최근 리니지W 매크로 프로젝트를 진행하며, 가장 어려웠던 부분이 GUI였습니다.
전체 코드가 413 라인인데 핵심 코드는 100줄 정도이니, GUI가 75%를 차지하는군요...😂
이걸 이용해 라인수를 최대한 줄여 강좌를 시작하려고 합니다.
사용 방법
① 필수 라이브러리 설치
pip install opencv-python numpy pillow
② cvgui.py 다운로드
사용할 앱이 있는 폴더에 저장합니다.
더보기
더보기
#cvgui.py
import os
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import tkinter as tk
from tkinter import messagebox, simpledialog, filedialog
root = tk.Tk()
root.withdraw()
#tkinter는 메시지 입/출력용도로 사용. 윈도우 사용 안함.
def showMessage(msg):
messagebox.showinfo("Message", msg)
def askFloat():
return simpledialog.askfloat(title="Set value", prompt="Type value(float)")
def askInt():
return simpledialog.askinteger(title="Set value", prompt="Type value(int)")
def askStr():
return simpledialog.askstring(title="Set value", prompt="Type value(str)")
def askFile():
dir = os.getcwd()
return filedialog.askopenfilename(initialdir=dir, title="Select .txt file")
def addImage(background, foreground, x, y):
ret, mask = cv2.threshold(foreground[:,:,3], 1, 255, cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)
foreground = cv2.cvtColor(foreground, cv2.COLOR_BGRA2BGR)
h, w = foreground.shape[:2]
roi = background[y:y+h, x:x+w]
maskedFg = cv2.bitwise_and(foreground, foreground, mask=mask)
maskedBg = cv2.bitwise_and(roi, roi, mask=mask_inv)
added = maskedFg + maskedBg
background[y:y+h, x:x+w] = added
return background
def makeButton(text, width, height, color, textSize=30, border=True, textAlign="center"):
img = Image.new("RGBA", (width, height), (0,0,0,0))
draw = ImageDraw.Draw(img, "RGBA")
outline = color if border else (0,0,0,0)
draw.rounded_rectangle((0, 0, width-1, height-1), fill=(0,0,0,0), outline=outline, width=3, radius=width/10)
font = ImageFont.truetype("fonts/gulim.ttc", size=textSize)
textWidth, textHeight = draw.textsize(text, font=font)
if textWidth > width:
img = Image.new("RGBA", (textWidth, height), (0,0,0,0))
draw = ImageDraw.Draw(img, "RGBA")
outline = color if border else (0,0,0,0)
draw.rounded_rectangle((0, 0, width-1, height-1), fill=(0,0,0,0), outline=outline, width=3, radius=width/10)
if textAlign == "center":
draw.text(((width-textWidth)/2,(height-textHeight)/2), text, fill=color, font=font)
elif textAlign == "left":
draw.text((0,(height-textHeight)/2), text, fill=color, font=font)
elif textAlign == "right":
draw.text((width-textWidth,(height-textHeight)/2), text, fill=color, font=font)
del draw
#img.show()
#opencv형식으로 변환하여 리턴
image = cv2.cvtColor(np.array(img), cv2.COLOR_RGBA2BGRA)
#cv2.imshow("image", image)
#cv2.waitKey(0)
return image
#PIL로 버튼 이미지 만드는 버전.
#한글 입력 가능
class GUI():
def __init__(self, text, x, y, w, h, color=(255,255,255,100), colorOn=(45,230,90,100), textSize=30, border=True, toggle=False, textAlign="center", readOnly=False):
self.text, self.textSize = text, textSize
self.x, self.y = x, y
self.w, self.h = w, h
self.lastValue = ""
self.toggle = toggle
self.readOnly = readOnly
self.color, self.colorOn = color, colorOn
self.border = border
self.textAlign = textAlign
self.state = False
self.make()
def make(self):
self.btn = makeButton(self.text, self.w, self.h, self.color, self.textSize, self.border, self.textAlign)
self.btnHover = makeButton(self.text, self.w, self.h, self.colorOn, self.textSize, self.border, self.textAlign)
self.btnClick = makeButton(self.text, self.w, self.h, self.colorOn, self.textSize, self.border, self.textAlign)
def changeText(self, text):
self.text = text
self.btn = makeButton(self.text, self.w, self.h, self.color, self.textSize, self.border, self.textAlign)
self.btnHover = makeButton(self.text, self.w, self.h, self.colorOn, self.textSize, self.border, self.textAlign)
self.btnClick = makeButton(self.text, self.w, self.h, self.colorOn, self.textSize, self.border, self.textAlign)
def add(self, frame, mouse):
self.hover = self.x <= mouse["x"] <= (self.x + self.w) and self.y <= mouse["y"] <= (self.y + self.h)
if self.readOnly:
addImage(frame, self.btn, self.x, self.y)
return
if not self.toggle:
if self.hover and mouse["down"]:
value = "DOWN"
addImage(frame, self.btnClick, self.x, self.y+2)
elif self.hover or self.state:
value = "HOVER"
addImage(frame, self.btnHover, self.x, self.y)
else:
value = "NORMAL"
addImage(frame, self.btn, self.x, self.y)
else:
value = "DOWN" if self.hover and mouse["down"] else "NORMAL"
if self.state:
addImage(frame, self.btnHover, self.x, self.y)
else:
addImage(frame, self.btn, self.x, self.y)
if self.lastValue != "DOWN" and value == "DOWN":
self.lastValue = value
if self.toggle:
self.state = not self.state
return "CLICK"
else:
self.lastValue = value
return value
③ 사용 예제
더보기
더보기
# gui.py
import cv2
import numpy as np
import cvgui
# 마우스 이벤트를 위한 딕셔너리.
# 마우스 콜백함수에 넣어서 항상 업데이트되도록 한다.
mouseValue = {"x": 0, "y": 0, "down": False}
def mouseCallback(event, x, y, flags, param):
mouseValue["x"], mouseValue["y"] = x, y
mouseValue["down"] = True if flags == cv2.EVENT_FLAG_LBUTTON else False
# 사용할 창의 이름을 MANDLOH로 정하고, 마우스 콜백을 지정한다.
win_title = "MANDLOH"
cv2.namedWindow(win_title)
cv2.setMouseCallback(win_title, mouseCallback)
# (height, width, channel) 300x300x3 크기의 빈 이미지를 생성해 배경으로 사용한다.
frame = np.zeros((150, 300, 3), np.uint8)
# 사용 예. 메인루프 진입 전 생성해 준다.
title = cvgui.GUI(text="파이썬 초보를 위한 OpenCV GUI.", x=5, y=5, w=100, h=40, textSize=15 ,border=False, textAlign="left", readOnly=True)
info = cvgui.GUI(text="q키나 창의 x버튼을 눌러 종료할 수 있습니다.", x=5, y=25, w=100, h=40, textSize=15 ,border=False, textAlign="left", readOnly=True)
btn = cvgui.GUI(text="버튼", x=10, y=60, w=100, h=40)
toggle = cvgui.GUI(text="토글", x=120, y=60, w=100, h=40, toggle=True)
text = cvgui.GUI(text="a 키를 눌러 텍스트를 바꿀 수 있습니다.", x=10, y=100, w=100, h=40, textSize=15 ,border=False, textAlign="left", readOnly=True)
maker = cvgui.GUI(text="만든이 : 만들오", x=190, y=130, w=100, h=20, textSize=12, textAlign="right" ,border=False, readOnly=True)
# 메인루프
while True:
# 배경 색상을 칠한다. OpenCV는 BGR(Blue,Green, Red) 순서.
frame[:] = (90, 90, 90)
# 생성한 GUI들을 frame에 추가한다.
# .add(frame, mouseValue) 후 리턴값들
# NORMAL : 선택되지 않은 상태
# HOVER : 마우스가 위에 있는 상태
# CLICK : 클릭한 경우(1회)
# DOWN : 마우스로 누르고 있는 상태
title.add(frame, mouseValue)
info.add(frame, mouseValue)
btn_value = btn.add(frame, mouseValue)
if btn_value != "NORMAL" and btn_value != "HOVER":
print(btn_value)
if toggle.add(frame, mouseValue) == "CLICK": print("HELLO")
text.add(frame, mouseValue)
if maker.add(frame, mouseValue) == "DOWN":
print("만들오입니다.")
cv2.imshow(win_title, frame)
key = cv2.waitKey(33)
#q키 또는 X버튼을 누르면 종료
if key == ord("q") or cv2.getWindowProperty(win_title, cv2.WND_PROP_VISIBLE) < 1:
break
elif key == ord("a"):
msg = cvgui.askStr()
text.changeText(msg)
cv2.destroyAllWindows()
사용 관련 궁금하신 부분은 댓글을 남겨주세요.
감사합니다.
[끝].
728x90
'소프트웨어 > 파이썬' 카테고리의 다른 글
[파이썬] OpenCV 이미지 서치 (Template matching) (8) | 2022.02.09 |
---|---|
[파이썬]PySimpleGUI - 간략한 소개 (6) | 2022.02.07 |
[파이썬] 비활성 키 입력 코드 공유 (6) | 2022.01.13 |
[파이썬] Pyinstaller dll 포함한 빌드 - 실패공유 (7) | 2022.01.13 |
[파이썬] ESP32-CAM 스트리밍 영상 불러오기(Tkinter, OpenCV) (8) | 2021.07.02 |
댓글