基于PySimpleGUI的OpenCV基本操作
欢迎交流学习
功能描述
使用PySimpleGUI模块,对OpenCV基本功能进行可视化展示,主要包括文章来源:https://www.toymoban.com/news/detail-774038.html
- 图像通道转换(HSV,彩色通道,灰度通道)
- 图像大小,对比度,亮度的调整
- HSV模式下的色彩追踪(色相,饱和度,明度)
- 图像直方图(均值化,CLAHE)
- 图像的模糊处理(中值模糊,高斯模糊,双边模糊)
- 图像的二值化操作(均值法,高斯法,OTSU法等)
- 图像的形态学操作(腐蚀,膨胀,开运算,闭运算)
- 图像轮廓的查找功能
- 图像拖拽功能
模块功能
import random
import PySimpleGUI as psg
import numpy as np
import cv2
设计思路
代码结构
"""
@author:bjxyc@qq.com
@file:chapter_13_cv2gui.py
@time:2023/12/04
Python Version:
pip国内源:
清华大学:https://pypi.tuna.tsinghua.edu.cn/simple
阿里云:https://mirrors.aliyun.com/pypi/simple/
"""
import random
import PySimpleGUI as psg
import numpy as np
import cv2
class MyApp:
def __init__(self):
self.img_copy_contours = None
self.gr_img = None
self.img = None
self.img_copy = None
self.img_result = None
self.img_location_y = 0
self.img_location_x = 0
self.value = None
self.event = None
self.screen_width = 1920
self.screen_height = 1080
# widget list
self.np_file_path = psg.Input(disabled=True, size=(20, 5))
self.bt_file_path = psg.FileBrowse(button_text='选择文件', key='file_path')
self.bt_file_read = psg.Button(button_text='读取文件', key='file_read')
# 通道
self.rd_gray = psg.Radio('黑白', 'is_color', key='-gray-', enable_events=True)
self.rd_hsv = psg.Radio('HSV', 'is_color', key='-hsv-', enable_events=True)
self.rd_color = psg.Radio('彩色', 'is_color', key='-color-', default=True, enable_events=True)
# 直方图
self.ck_equalizeHist = psg.Checkbox('均值化', key='-equalizeHist-', enable_events=True)
self.ck_CLAHE = psg.Checkbox('CLAHE', key='-CLAHE-', enable_events=True)
# 阈值类
self.rd_threshold_NONE = psg.Radio('无', 'is_threshold', key='-threshold_NONE-', default=True, enable_events=True)
self.rd_threshold_BINARY = psg.Radio('二值化', 'is_threshold', key='-BINARY-', enable_events=True)
self.rd_threshold_BINARY_INV = psg.Radio('反二值化', 'is_threshold', key='-BINARY_INV-', enable_events=True)
self.rd_threshold_TRUNC = psg.Radio('TRUNC', 'is_threshold', key='-TRUNC-', enable_events=True)
self.rd_threshold_TOZERO = psg.Radio('TOZERO', 'is_threshold', key='-TOZERO-', enable_events=True)
self.rd_threshold_TOZERO_INV = psg.Radio('TOZERO_INV', 'is_threshold', key='-TOZERO_INV-', enable_events=True)
self.rd_threshold_MEAN = psg.Radio('均值', 'is_threshold', key='-MEAN-', enable_events=True)
self.rd_threshold_GAUSSIAN = psg.Radio('高斯', 'is_threshold', key='-GAUSSIANV-', enable_events=True)
self.rd_threshold_OTSU = psg.Radio('OTSU', 'is_threshold', key='-OTSU-', enable_events=True)
# 图像平滑
self.rd_blur_none = psg.Radio('NONE', 'is_blur', default=True, key='-blur_NONE-', enable_events=True)
self.rd_blur_Gaussian = psg.Radio('高斯', 'is_blur', key='-GaussianBlur-', enable_events=True)
self.rd_blur_median = psg.Radio('中值', 'is_blur', key='-medianBlur-', enable_events=True)
self.rd_blur_bilateral = psg.Radio('双边', 'is_blur', key='-bilateral-', enable_events=True)
# 形态学
self.rd_morphology_none = psg.Radio('NONE', 'is_morphology', key='-morphology_NONE-', default=True, enable_events=True)
self.rd_morphology_erode = psg.Radio('腐蚀', 'is_morphology', key='-erode-', enable_events=True)
self.rd_morphology_dilate = psg.Radio('膨胀', 'is_morphology', key='-dilate-', enable_events=True)
# self.rd_morphology_open = psg.Radio('开运算', 'is_morphology', key='-open-', enable_events=True)
# self.rd_morphology_close = psg.Radio('闭运算', 'is_morphology', key='-close-', enable_events=True)
self.rd_morphology_open = psg.Checkbox('开运算', key='-open-', enable_events=True)
self.rd_morphology_close = psg.Checkbox('闭运算', key='-close-', enable_events=True)
# 轮廓
self.rd_find_contour_none = psg.Radio('NONE', 'is_find_contour', key='-find_contour_none-', default=True, enable_events=True)
self.rd_find_contour = psg.Radio('找轮廓', 'is_find_contour', key='-find_contour-', enable_events=True)
self.rd_find_contour_boundingRect = psg.Checkbox('外接矩', key='-boundingRect-', enable_events=True)
self.rd_find_contour_minEnclosingCircle = psg.Checkbox('外接圆', key='-minEnclosingCircle-', enable_events=True)
self.rd_find_contour_ellipse = psg.Checkbox('外接椭', key='-ellipse-', enable_events=True)
self.tt_contour_area = psg.Text()
self.tt_contour_length = psg.Text()
self.sl_img_scale = psg.Slider((0.01, 2), default_value=1, orientation='horizontal', resolution=0.01, key='-scale-', enable_events=True)
self.sl_img_contrast = psg.Slider((0, 2), default_value=1, orientation='horizontal', resolution=0.01, key='-contrast-', enable_events=True)
self.sl_img_light = psg.Slider((-255, 255), default_value=0, orientation='horizontal', key='-light-', enable_events=True)
self.sl_img_hsv_lower_h = psg.Slider((0, 255), default_value=0, orientation='horizontal', key='-hsv_lower_h-', enable_events=True)
self.sl_img_hsv_lower_s = psg.Slider((0, 255), default_value=0, orientation='horizontal', key='-hsv_lower_s-', enable_events=True)
self.sl_img_hsv_lower_v = psg.Slider((0, 255), default_value=0, orientation='horizontal', key='-hsv_lower_v-', enable_events=True)
self.sl_img_hsv_upper_h = psg.Slider((0, 180), default_value=180, orientation='horizontal', key='-hsv_upper_h-', enable_events=True)
self.sl_img_hsv_upper_s = psg.Slider((0, 255), default_value=255, orientation='horizontal', key='-hsv_upper_s-', enable_events=True)
self.sl_img_hsv_upper_v = psg.Slider((0, 255), default_value=255, orientation='horizontal', key='-hsv_upper_v-', enable_events=True)
self.sl_img_threshold = psg.Slider((0, 255), default_value=127, orientation='horizontal', key='-threshold-', enable_events=True)
self.sl_img_blue = psg.Slider((0, 10), default_value=2, orientation='horizontal', key='-blur-', enable_events=True)
self.sl_img_morphology = psg.Slider((0, 10), default_value=2, orientation='horizontal', key='-morphology-', enable_events=True)
self.sl_img_morphology_times = psg.Slider((0, 10), default_value=2, orientation='horizontal', key='-morphology_times-', enable_events=True)
self.mg_img_show = psg.Image(key='-image-')
self.gr_img_show = psg.Graph(canvas_size=(1400, 1000), graph_bottom_left=(0, 1000), graph_top_right=(1400, 0), background_color="white",
key="-GRAPH-", drag_submits=True, enable_events=True)
# function dict
self.func_dict = {
'file_read': self.read_img,
'-scale-': self.img_handler,
'-contrast-': self.img_handler,
'-light-': self.img_handler,
'-gray-': self.img_handler,
'-color-': self.img_handler,
'-hsv-': self.img_handler,
'-hsv_lower_h-': self.img_handler,
'-hsv_lower_s-': self.img_handler,
'-hsv_lower_v-': self.img_handler,
'-hsv_upper_h-': self.img_handler,
'-hsv_upper_s-': self.img_handler,
'-hsv_upper_v-': self.img_handler,
'-threshold_NONE-': self.img_handler,
'-BINARY-': self.img_handler,
'-BINARY_INV-': self.img_handler,
'-TRUNC-': self.img_handler,
'-TOZERO-': self.img_handler,
'-TOZERO_INV-': self.img_handler,
'-threshold-': self.img_handler,
'-MEAN-': self.img_handler,
'-GAUSSIANV-': self.img_handler,
'-OTSU-': self.img_handler,
'-blur_NONE-': self.img_handler,
'-GaussianBlur-': self.img_handler,
'-medianBlur-': self.img_handler,
'-bilateral-': self.img_handler,
'-blur-': self.img_handler,
'-morphology_NONE-': self.img_handler,
'-erode-': self.img_handler,
'-dilate-': self.img_handler,
'-morphology-': self.img_handler,
'-morphology_times-': self.img_handler,
'-open-': self.img_handler,
'-close-': self.img_handler,
'-find_contour_none-': self.img_handler,
'-find_contour-': self.img_handler,
'-boundingRect-': self.img_handler,
'-minEnclosingCircle-': self.img_handler,
'-ellipse-': self.img_handler,
'-equalizeHist-': self.img_handler,
'-CLAHE-': self.img_handler,
}
# Create windows
# self.win = psg.Window('CV2GUI', layout=self.layout(), size=(self.screen_width, self.screen_height), no_titlebar=True)
self.win = psg.Window('CV2GUI', layout=self.layout(), size=(self.screen_width, self.screen_height))
def img_handler(self):
# 参数获取
f = self.value['-scale-']
alpha = self.value['-contrast-']
beta = self.value['-light-']
hsv_lower_h = self.value['-hsv_lower_h-']
hsv_lower_s = self.value['-hsv_lower_s-']
hsv_lower_v = self.value['-hsv_lower_v-']
hsv_upper_h = self.value['-hsv_upper_h-']
hsv_upper_s = self.value['-hsv_upper_s-']
hsv_upper_v = self.value['-hsv_upper_v-']
threshold = self.value['-threshold-']
blur = int(self.value['-blur-'] * 2 + 1)
morphology = int(self.value['-morphology-'] * 2 + 1)
morphology_times = int(self.value['-morphology_times-'] * 2 + 1)
# 图像通道
if self.value['-gray-']:
self.img_result = cv2.cvtColor(self.img_copy, cv2.COLOR_BGR2GRAY)
if self.value['-color-']:
self.img_result = self.img_copy
if self.value['-hsv-']:
self.img_result = cv2.cvtColor(self.img_copy, cv2.COLOR_BGR2HSV)
lower_blue = np.array([hsv_lower_h, hsv_lower_s, hsv_lower_v])
upper_blue = np.array([hsv_upper_h, hsv_upper_s, hsv_upper_v])
mask = cv2.inRange(self.img_result, lower_blue, upper_blue)
self.img_result = cv2.bitwise_and(self.img_copy, self.img_copy, mask=mask)
# 图像缩放
# self.img_result = cv2.resize(self.img_result, (0, 0), fx=f, fy=f)
# 对比度,亮度
self.img_result = cv2.convertScaleAbs(self.img_result, alpha=alpha, beta=beta)
if self.value['-equalizeHist-']:
try:
self.img_result = cv2.equalizeHist(self.img_result)
except cv2.error as e:
psg.popup_error('黑白通道有效')
if self.value['-CLAHE-']:
try:
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
self.img_result = clahe.apply(self.img_result)
except cv2.error as e:
psg.popup_error('黑白通道有效')
# 图像平滑
if self.value['-blur_NONE-']:
pass
if self.value['-GaussianBlur-']:
self.img_result = cv2.GaussianBlur(self.img_result, (blur, blur), 0)
if self.value['-medianBlur-']:
self.img_result = cv2.medianBlur(self.img_result, blur)
if self.value['-bilateral-']:
self.img_result = cv2.bilateralFilter(self.img_result, blur, 75, 75)
# 阈值类
if self.value['-threshold_NONE-']:
pass
if self.value['-BINARY-']:
_, self.img_result = cv2.threshold(self.img_result, threshold, 255, cv2.THRESH_BINARY)
if self.value['-BINARY_INV-']:
_, self.img_result = cv2.threshold(self.img_result, threshold, 255, cv2.THRESH_BINARY_INV)
if self.value['-TRUNC-']:
_, self.img_result = cv2.threshold(self.img_result, threshold, 255, cv2.THRESH_TRUNC)
if self.value['-TOZERO-']:
_, self.img_result = cv2.threshold(self.img_result, threshold, 255, cv2.THRESH_TOZERO)
if self.value['-TOZERO_INV-']:
_, self.img_result = cv2.threshold(self.img_result, threshold, 255, cv2.THRESH_TOZERO_INV)
if self.value['-MEAN-']:
try:
self.img_result = cv2.adaptiveThreshold(self.img_result, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
except cv2.error as e:
psg.popup_error('黑白通道有效')
if self.value['-GAUSSIANV-']:
try:
self.img_result = cv2.adaptiveThreshold(self.img_result, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
except cv2.error as e:
psg.popup_error('黑白通道有效')
if self.value['-OTSU-']:
try:
_, self.img_result = cv2.threshold(self.img_result, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
except cv2.error as e:
psg.popup_error('黑白通道有效')
# 形态学
if self.value['-morphology_NONE-']:
pass
if self.value['-erode-']:
kernel = np.ones((morphology, morphology), np.uint8)
self.img_result = cv2.erode(self.img_result, kernel, iterations=morphology_times)
if self.value['-dilate-']:
kernel = np.ones((morphology, morphology), np.uint8)
self.img_result = cv2.dilate(self.img_result, kernel, iterations=morphology_times)
if self.value['-open-']:
kernel = np.ones((morphology, morphology), np.uint8)
self.img_result = cv2.morphologyEx(self.img_result, cv2.MORPH_OPEN, kernel)
if self.value['-close-']:
kernel = np.ones((morphology, morphology), np.uint8)
self.img_result = cv2.morphologyEx(self.img_result, cv2.MORPH_CLOSE, kernel)
# 轮廓
if self.value['-find_contour_none-']:
pass
if self.value['-find_contour-']:
contours, hierarchy = cv2.findContours(self.img_result, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print(len(contours))
self.img_copy_contours = self.img_copy.copy()
for i in range(len(contours)):
b = random.randint(0, 255)
g = random.randint(0, 255)
r = random.randint(0, 255)
cv2.drawContours(self.img_copy_contours, contours, i, (b, g, r), 2)
if self.value['-minEnclosingCircle-']:
(x, y), radius = cv2.minEnclosingCircle(contours[i])
center = (int(x), int(y))
radius = int(radius)
cv2.circle(self.img_copy_contours, center, radius, (b, g, r), 2)
if self.value['-ellipse-']:
ellipse = cv2.fitEllipse(contours[i])
cv2.ellipse(self.img_copy_contours, ellipse, (b, g, r), 2)
if self.value['-boundingRect-']:
x, y, w, h = cv2.boundingRect(contours[i])
cv2.rectangle(self.img_copy_contours, (x, y), (x + w, y + h), (b, g, r), 2)
self.img_result = self.img_copy_contours
# 图像缩放
self.img_result = cv2.resize(self.img_result, (0, 0), fx=f, fy=f)
self.show_img(self.img_result)
pass
def show_img(self, img):
img_bytes = cv2.imencode('.png', img)[1].tobytes()
self.gr_img_show.delete_figure(self.gr_img)
self.gr_img = self.gr_img_show.draw_image(data=img_bytes, location=(self.img_location_x, self.img_location_y))
self.gr_img_show.update(background_color="white")
def read_img(self):
path = self.value['file_path']
self.img = cv2.imread(path)
self.img_copy = self.img.copy()
self.show_img(self.img_copy)
def layout(self):
layout_col_1 = [
[psg.Exit()],
[self.np_file_path, self.bt_file_path, self.bt_file_read],
[psg.Text('通 道:', size=(9, 1), font=('黑体', 12)), self.rd_gray, self.rd_color, self.rd_hsv],
[psg.Text('缩 放:', size=(9, 1), font=('黑体', 12)), self.sl_img_scale],
[psg.Text('对比度:', size=(9, 1), font=('黑体', 12)), self.sl_img_contrast],
[psg.Text('亮 度:', size=(9, 1), font=('黑体', 12)), self.sl_img_light],
[psg.Text('色 相:', size=(9, 1), font=('黑体', 12)), self.sl_img_hsv_lower_h, self.sl_img_hsv_upper_h],
[psg.Text('饱和度:', size=(9, 1), font=('黑体', 12)), self.sl_img_hsv_lower_s, self.sl_img_hsv_upper_s],
[psg.Text('明 度:', size=(9, 1), font=('黑体', 12)), self.sl_img_hsv_lower_v, self.sl_img_hsv_upper_v],
[psg.Text('直方图:', size=(9, 1), font=('黑体', 12)), self.ck_equalizeHist, self.ck_CLAHE],
[psg.Text('平滑类:', size=(9, 1), font=('黑体', 12)), self.rd_blur_none, self.rd_blur_median, self.rd_blur_Gaussian,
self.rd_blur_bilateral],
[psg.Text('卷积核:', size=(9, 1), font=('黑体', 12)), self.sl_img_blue],
[psg.Text('阈值类:', size=(9, 1), font=('黑体', 12)), self.rd_threshold_NONE, self.rd_threshold_MEAN, self.rd_threshold_GAUSSIAN,
self.rd_threshold_OTSU],
[self.rd_threshold_BINARY, self.rd_threshold_BINARY_INV, self.rd_threshold_TRUNC, self.rd_threshold_TOZERO, self.rd_threshold_TOZERO_INV],
[psg.Text('阈 值:', size=(9, 1), font=('黑体', 12)), self.sl_img_threshold],
[psg.Text('形态学:', size=(9, 1), font=('黑体', 12)), self.rd_morphology_none, self.rd_morphology_erode, self.rd_morphology_dilate,
self.rd_morphology_open, self.rd_morphology_close],
[psg.Text('卷积核:', size=(9, 1), font=('黑体', 12)), self.sl_img_morphology],
[psg.Text('迭代数:', size=(9, 1), font=('黑体', 12)), self.sl_img_morphology_times],
[psg.Text('找轮廓:', size=(9, 1), font=('黑体', 12)), self.rd_find_contour_none, self.rd_find_contour,
self.rd_find_contour_boundingRect, self.rd_find_contour_minEnclosingCircle, self.rd_find_contour_ellipse],
]
layout_col_2 = [
[self.gr_img_show]
]
layout = [
[psg.Column(layout_col_1, size=(500, 900)), psg.Column(layout_col_2)]
]
return layout
def show(self):
x1, gx, y1, gy = None, None, None, None
while True:
self.event, self.value = self.win.read()
print(self.event, self.value)
if self.event in [None, 'Exit']:
self.win.close()
break
if self.event in self.func_dict:
self.func_dict[self.event]()
# 鼠标跟随事件
if self.event in ['-GRAPH-']:
x, y = self.value['-GRAPH-']
if not x1 or not y1:
x1, y1 = x, y
gx, gy = self.gr_img_show.get_bounding_box(self.gr_img)[0]
dx, dy = x - x1, y - y1
self.img_location_x = gx + dx
self.img_location_y = gy + dy
self.gr_img_show.relocate_figure(self.gr_img, gx + dx, gy + dy)
if self.event in ['-GRAPH-+UP']:
x1, gx, y1, gy = None, None, None, None
if __name__ == '__main__':
app = MyApp()
app.show()
GUI示例
文章来源地址https://www.toymoban.com/news/detail-774038.html
到了这里,关于基于PySimpleGUI的Opencv基本操作(Python实现)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!