如何仅提取图像的圆形ROI部分并在Python OpenCV GUI的Tkinter窗口中单击按钮以显示圆的半径

问题描述 投票:0回答:1

提取圆形ROI并在Tkinter标签中显示圆的半径

我正在向该社区的python专家寻求帮助。我在Stackexchange以及Github社区中搜索了有关我的问题的信息。但是我没有发现任何帮助。我创建了一个Tkinter GUI。在此GUI中,我可以从目标文件夹上载我的图像。在“评估”的“选择”部分,我编写了一个脚本,通过该脚本,可以自动查看圆形部分中的ROI区域。 GUI显示在该问题的底部。

需要帮助的部分:我在创建脚本时遇到了麻烦:

  1. 当我单击上传ROI按钮时,只有选定的ROI部分的图片保存在目标文件夹中,即path ='数据/图像/'+名称+'_'+方法+ ext
  2. 我可以在Tkinter GUI的某处查看圆的半径。

import os
import shutil
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import cv2
import numpy as np
from PIL import Image
from PIL import ImageTk


class Button:

    def __init__(self, root, frame3):
        self.root = root
        self.frame3 = frame3
        self.radio_var = tk.IntVar()
        self.path_selected = 'none'
        self.paths = []
        self.radio_handle = []
        self.check_value = []

    def on_click_select_button(self, fname_label):
        print('select button clicked')
        fileType = [('jpg/png file', ('*.jpg', '*.png'))]
        self.path_selected = filedialog.askopenfilename(filetypes=fileType)
        fname_label['text'] = os.path.basename(self.path_selected)

    def on_click_upload_button(self, path='None', image='None'):
        print('upload button clicked')

        if path == 'None':
            path = self.path_selected
        else:
            cv2.imwrite(path, image)

        if path in self.paths:
            messagebox.showerror('Upload Error', '"'
                                 + path
                                 + '"' + ' is already uploaded.')
        else:
            self.paths.append(path)
            self.create_radio_button(path)

    def on_click_show_button(self, method):
        print('showButton clicked')
        image = cv2.imread(self.paths[self.radio_var.get()])
        image = self.ROI(image, method)
        file_name = os.path.basename(self.paths[self.radio_var.get()])
        name, ext = os.path.splitext(file_name)
        path = 'Data/images/' + name + '_' + method + ext
        self.open_image_window(path, image)


    def ROI(self, image, method):
        if method == 'ROI':
            image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
            blimage = cv2.medianBlur(image, 15)
            circles = cv2.HoughCircles(blimage, cv2.HOUGH_GRADIENT, 1, 255, param1=100, param2=60, minRadius=0,
                                       maxRadius=0)

            if circles is not None:
                circles = np.uint16(np.around(circles))
                for i in circles[0, :]:
                    cv2.circle(image, (i[0], i[1]), i[2], (0, 255, 0), 6)
                    cv2.circle(image, (i[0], i[1]), 2, (0, 0, 255), 3)
            cv2.waitKey()

        else:
            print('method is wrong')

        return image

    def create_radio_button(self, path):

        image = cv2.imread(path)
        # image = cv2.resize(image,(120,120))
        image = self.scale_to_height(image, 120)
        image_tk = self.to_tk_image(image)

        radio_button = tk.Radiobutton(self.frame3, image=image_tk,
                                      value=len(self.radio_handle),
                                      variable=self.radio_var)
        self.radio_var.set(0)
        self.radio_handle.append(radio_button)
        self.check_value.append(self.radio_var)

        radio_button.grid(row=(len(self.radio_handle) - 1) // 3,
                          column=(len(self.radio_handle) - 1) % 3)
        self.root.mainloop()

    def open_image_window(self, path, image):

        if image.shape[0] > 300:
            image = self.scale_to_height(image, 300)

        img_win = tk.Toplevel(self.root)
        fname = os.path.basename(path)
        img_win.title(fname)
        img_canvas = tk.Canvas(img_win, width=image.shape[1],
                               height=image.shape[0])
        img_canvas.pack()
        image_tk = self.to_tk_image(image)
        img_canvas.create_image(0, 0, image=image_tk, anchor='nw')

        uploadButton2 = tk.Button(img_win, text='Upload ROI',
                                  command=lambda: self.on_click_upload_button(path, image))
        uploadButton2.pack()

        self.root.mainloop()

    def to_tk_image(self, image_bgr):
        image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
        image_pil = Image.fromarray(image_rgb)
        image_tk = ImageTk.PhotoImage(image_pil)

        return image_tk

    def scale_to_height(self, img, height):
        scale = height / img.shape[0]
        return cv2.resize(img, dsize=None, fx=scale, fy=scale)


if __name__ == '__main__':

    root = tk.Tk()
    root.title('Image GUI')
    root.geometry('1280x960')
    # scrollbar = Scrollbar(root)
    # scrollbar.pack(side='right', fill=Y)

    #######################################Reference#########################################################
    dir = 'Data/images'
    if os.path.exists(dir):
        shutil.rmtree(dir)
    os.makedirs(dir)
    # os.makedirs('Data/images', exist_ok=True)
    pw_left = tk.Frame(root, relief='ridge', borderwidth=6)
    pw_left.pack(side='left', anchor='nw')
    pw_right = tk.Frame(root, relief='ridge', borderwidth=6)
    pw_right.pack(side='top', anchor='e')
    frame1 = tk.Frame(pw_left, bd=2, relief="ridge")
    frame1.pack(fill=tk.X)
    frame2 = tk.LabelFrame(pw_left, bd=2, relief="ridge", text='options')
    frame2.pack(fill=tk.X)
    frame3 = tk.LabelFrame(pw_right, bd=1, text='Uploaded images')
    frame3.pack(fill=tk.Y)
    button = Button(root, frame3)
    label = tk.Label(frame1, text='File:')
    label.pack(fill=tk.X)
    file_name_label = tk.Label(frame1, text='-----not selected-----', width=50, bg='white')
    file_name_label.pack(fill=tk.X)
    select_button = tk.Button(frame1, text='select', command=lambda: button.on_click_select_button(file_name_label))
    select_button.pack(side='left', padx=5, pady=5)
    uploadButton = tk.Button(frame1, text='Upload', command=lambda: button.on_click_upload_button())
    uploadButton.pack(side='left', padx=5, pady=5)

    # gray button
    ROI_label = tk.Label(frame2, text='Select for Evaluation')
    ROI_label.grid(row=0, column=0)
    ROI_show = tk.Button(frame2, text='show', command=lambda: button.on_click_show_button('ROI'))
    ROI_show.grid(row=0, column=1)

    root.mainloop()

GUIenter image description here

python opencv user-interface tkinter roi
1个回答
2
投票

UPDATE:

我添加了变量border以计算x1,y1,x2,y2,因此现在它会以边界线进行裁剪。图片显示了不带border

的旧代码的结果

如果只有一个圆圈(x,y,r),则可以使用它来裁剪图像

image = image[y-r:y+r, x-r:x+r]

我在比图像大一圈的图像上进行测试,并且我必须使用int16而不是unit16来获得-165535)的170-171而不是y-r。添加我必须使用min(),max()to get 0instead-1`

def ROI(self, image, method):
    if method == 'ROI':
        image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        blimage = cv2.medianBlur(image, 15)
        circles = cv2.HoughCircles(blimage, cv2.HOUGH_GRADIENT, 1, 255, param1=100, param2=60, minRadius=0,
                                   maxRadius=0)
        if circles is not None:
            #print(circles)

            # need `int` instead of `uint` to correctly calculate `y-r` (to get `-1` instead of `65535`)
            circles = np.int16(np.around(circles)) 

            for x,y,r in circles[0, :]:
                print('x, y, r:', x, y, r)

                border = 6

                cv2.circle(image, (x, y), r, (0, 255, 0), border)
                cv2.circle(image, (x, y), 2, (0, 0, 255), 3)

                height, width = image.shape
                print('height, width:', height, width)

                # calculate region to crop
                x1 = max(x-r - border//2, 0)      # eventually  -border//2-1
                x2 = min(x+r + border//2, width)  # eventually  +border//2+1
                y1 = max(y-r - border//2, 0)      # eventually  -border//2-1
                y2 = min(y+r + border//2, height) # eventually  +border//2+1
                print('x1, x2:', x1, x2)
                print('y1, y2:', y1, y2)

                # crop image 
                image = image[y1:y2,x1:x2]
                print('height, width:', image.shape)
    else:
        print('method is wrong')

    return image

对于更多的圆,您必须首先计算用于所有圆的区域(获取drom所有圆的最小值x-ry-r和最大值x+ry+r)和下一个裁剪图像。

enter image description here

稍后我将尝试使用Alpha通道删除圆圈外的背景。


用于测试的图像(如果其他人想测试代码)

enter image description here


EDIT:我添加了创建带有白色圆圈的黑色图像以删除背景的代码。

def ROI(self, image, method):
    if method == 'ROI':
        image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        blimage = cv2.medianBlur(image, 15)
        circles = cv2.HoughCircles(blimage, cv2.HOUGH_GRADIENT, 1, 255, param1=100, param2=60, minRadius=0,
                                   maxRadius=0)
        if circles is not None:
            print(circles)
            circles = np.int16(np.around(circles)) # need int instead of uint to correctly calculate y-r (to get -1 instead of 65535)
            for x,y,r in circles[0, :]:
                print('x, y, r:', x, y, r)
                height, width = image.shape
                print('height, width:', height, width)

                border = 6

                cv2.circle(image, (x, y), r, (0, 255, 0), border)
                cv2.circle(image, (x, y), 2, (0, 0, 255), 3)

                mask = np.zeros(image.shape, np.uint8) # black background
                cv2.circle(mask, (x, y), r, (255), border)  # white mask for black border
                cv2.circle(mask, (x, y), r, (255), -1) # white mask for (filled) circle
                #image = cv2.bitwise_and(image, mask)  # image with black background
                image = cv2.bitwise_or(image, ~mask)  # image with white background

                x1 = max(x-r - border//2, 0)      # eventually  -border//2-1
                x2 = min(x+r + border//2, width)  # eventually  +border//2+1
                y1 = max(y-r - border//2, 0)      # eventually  -border//2-1
                y2 = min(y+r + border//2, height) # eventually  +border//2+1
                print('x1, x2:', x1, x2)
                print('y1, y2:', y1, y2)

                image = image[y1:y2,x1:x2]
                print('height, width:', image.shape)
    else:
        print('method is wrong')

    return image

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.