python - 如何使用 tkinter 比例修改图像?

标签 python python-2.7 opencv tkinter

我是 Python 和 OpenCV 的新手,所以如果我说/问的任何事情听起来不明智或不知情,我深表歉意。我们在我的数字图像处理类(class)中以一种非常具体的方式学到了东西,所以虽然我下面的代码有效,但我想大大提高它的速度,但主要是它的 UI。该代码的主要目的是创建绿屏图像的二进制 mask ,然后将其应用于图像以将前景与背景分开。至于功能:

keyer 根据所需的 mask 类型(colorDifference 或 chromaKey)创建输入图像的阈值 mask 。

thresholdAdjust 允许用户通过 Open CV UI 操纵阈值 slider 。它还会实时更新图像(当按下 Enter 键时)以反射(reflect)原始 mask 如何根据所选阈值发生变化。由于我对这些功能完全陌生,所以我看到的所有示例都使用了类似 tracker 的功能。不过我不确定是否有必要。

最后,keyMultiplier 将在 thresholdAdjust 中生成的 mask 应用于输入图像并显示分离的前景。

(没有必要通读所有的 keyer 代码,因为它可以正常工作,但速度有点慢。我把所有代码都包括在内以防万一。)

import numpy as np
import cv2

def keyer(image, matteType, threshold1, threshold2, maxCount=255):

    numberRows = image.shape[0]
    numberColumns = image.shape[1]

    if image.ndim > 2:
        numberBands = image.shape[2]
    else:
        numberBands == 1

    if numberBands != 3:
        raise RuntimeError('Input image must be an RGB image.')

    dataType = image.dtype

    matte = np.zeros((numberRows, numberColumns, 1))
    imageNorm = image / float(maxCount)

    if matteType == 'colorDifference':
        blue, green, red = cv2.split(imageNorm)

        for row in range(numberRows):
            for column in range(numberColumns):        
                pixel = green[row, column] - max(red[row, column], blue[row, column])

                matte[row, column] = pixel

    elif matteType == 'chromaKey':
        imageYCbCr = cv2.cvtColor(image, cv2.COLOR_BGR2YCrCb)
        imageYCbCrNorm = imageYCbCr / float(maxCount)
        Y, Cr, Cb = cv2.split(imageYCbCrNorm)
        CbScreen =  Cb[0, 0]
        CrScreen = Cr[0, 0]

        for row in range(numberRows):
            for column in range(numberColumns):
                pixel = float(np.sqrt(((CbScreen - Cb[row, column])**2) + \
                                      ((CrScreen - Cr[row, column])**2)))

                matte[row, column] = pixel

    else:
        raise RuntimeError('Please enter a valid "matteType".')

    matte = np.clip(matte, 0.0, 1.0)

    if matteType == 'colorDifference':
        matte = 1.0 - matte

    for row in range(numberRows):
        for column in range(numberColumns):

            if matte[row, column] < threshold1:
                matte[row, column] = 0
            elif matte[row, column] > threshold2:
                matte[row, column] = 1
            else:
                matte[row, column] = ((matte[row, column] - threshold1) / \
                                      (threshold2 - threshold1))

    return (matte * float(maxCount)).astype(dataType)

def tracker(*arg):
    pass

def thresholdAdjust(image, matteType, maxCount):
    numberRows = image.shape[0]
    numberColumns = image.shape[1]

    if matteType == 'colorDifference':
        matteName = '(Color Difference)'
    else:
        matteName = '(Chroma Key)'

    windowName = 'Adjust Matte Parameters ' + matteName        
    threshold1TrackbarName = 'Clip Black'
    threshold2TrackbarName = 'Clip White'

    cv2.namedWindow(windowName)
    cv2.createTrackbar(threshold1TrackbarName, windowName, 0, maxCount, tracker)
    cv2.createTrackbar(threshold2TrackbarName, windowName, 0, maxCount, tracker)

    cv2.setTrackbarPos(threshold2TrackbarName, windowName, maxCount)

    while True:
        threshold1 = (cv2.getTrackbarPos(threshold1TrackbarName, windowName) / \
                      float(maxCount))
        threshold2 = (cv2.getTrackbarPos(threshold2TrackbarName, windowName) / \
                      float(maxCount))

        window = keyer(image, matteType, threshold1, threshold2, maxCount)
        cv2.imshow(windowName, window)

        k = cv2.waitKey(0)
        if k == 27:
            break

    cv2.destroyAllWindows()

    return threshold1, threshold2

def keyMultiplier(image, matteType, maxCount):
    dataType = image.dtype

    threshold1, threshold2 = thresholdAdjust(image, matteType, maxCount)
    matte = keyer(image, matteType, threshold1, threshold2, maxCount)

    return ((cv2.GaussianBlur((matte / float(maxCount)), (3, 3), 0)[..., np.newaxis]) * \
            image).astype(dataType)


if __name__ == '__main__':

    import cv2

    filename = 'C:/Users/Matt/Documents/circle_test.tif'

    image = cv2.imread(filename)


    difference = keyMultiplier(image, matteType='colorDifference', maxCount=255) # 163, 215
    chroma = keyMultiplier(image, matteType='chromaKey', maxCount=255) # 25, 50

    cv2.imshow('Original Image', image)
    cv2.imshow('Color Difference', difference)
    cv2.imshow('Chroma Key', chroma)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

我想用这段代码做的是将它与 Tkinter UI 一起使用,因为它比 OpenCV 更好并且有更多选项。我一直在查看大量线程,但没有找到任何可以帮助我理解我的具体问题的东西。我确实在这个 Stack Overflow 上找到了一个答案,它提供了以下代码,我重新格式化以反射(reflect)我的代码的目的。每次释放 slider 控件时,它都会打印 slider 的值。

from Tkinter import *

class App():
    def __init__(self):
        self.root = Tk()
        self.root.title('Adjust Matte Parameters')

        self.clipBlack = Scale(self.root, from_=0, to=255, orient=HORIZONTAL, \
                               label='Clip Black', length=500)
        self.clipBlack.bind("<ButtonRelease-1>", self.updateValue)
        self.clipBlack.pack(anchor=CENTER)

        self.clipWhite = Scale(self.root, from_=0, to=255, orient=HORIZONTAL, \
                               label='Clip White', length=500)
        self.clipWhite.set(255)
        self.clipWhite.bind("<ButtonRelease-1>", self.updateValue)
        self.clipWhite.pack(anchor=CENTER)

        self.root.mainloop()

    def updateValue(self, event):
        print 'Clip Black = ' + str(self.clipBlack.get())
        print 'Clip White = ' + str(self.clipWhite.get())

app = App()

但是,这会引发更多问题。虽然我真的很喜欢它会立即检索 slider 位置,但它所做的只是打印它们。我希望将这些值插入到 keyer 函数中,正如我在 thresholdAdjust 中实现的那样。这样,我想更新图像,但我没有运气在 Tkinter 窗口打开时使用 OpenCV 显示图像,因为“root.mainloop()”行似乎不允许它。我找到了一些关于同时显示 Tkinter 和 OpenCV 窗口以及使用 Tkinter 显示图像的帮助,但这些选项无关紧要,直到我可以使用新的阈值更新 mask 。

我也不太明白如何处理其中的类和函数。我只习惯于通过创建如下所示的测试工具来运行代码

if __name__ == '__main__': 

在第一个代码块中,所以我不知道如何在不对其进行硬编码的情况下确定我想要使用的图像和 mask 类型。

如果有人能帮助我,那将不胜感激!我知道很多,我很乐意消除任何困惑。一旦我知道如何使用 slider 完成此操作,我应该也能够将它应用于单选按钮。同样,我想:

  • 使用阈值 slider 更新我的keyer功能
  • 实时更新 mask ,使用 OpenCV 或 Tkinter,以更容易的为准
  • 了解如何(如果适用)更改类中的输入图像和其他变量

谢谢!

最佳答案

好的,所以我已经为此倾注了几个小时,我想我终于找到了一个解决方案(你需要一个 .png 文件名 picture.png为了运行下面的相同目录):

from tkinter import *
from PIL import Image, ImageTk

class App(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)
        self.frame1 = Frame(self)
        self.frame2 = Frame(self)
        self.original = Image.open('picture.png')
        self.image = ImageTk.PhotoImage(self.original)
        self.display = Canvas(self.frame1)
        self.xscale = Scale(self.frame2, from_=1, to=1000, orient=HORIZONTAL, command=self.resize)
        self.yscale = Scale(self.frame2, from_=1, to=1000, orient=VERTICAL, command=self.resize)
        self.display.pack(fill=BOTH, expand=1)
        self.xscale.pack()
        self.yscale.pack()
        self.pack(fill=BOTH, expand=1)
        self.frame1.pack(fill=BOTH, expand=1)
        self.frame2.pack()
        self.bind("<Configure>", self.resize)

    def resize(self, *args):
        size = (self.xscale.get(), self.yscale.get())
        resized = self.original.resize(size,Image.ANTIALIAS)
        self.image = ImageTk.PhotoImage(resized)
        self.display.delete("IMG")
        self.display.create_image(self.display.winfo_width()/2, self.display.winfo_height()/2, anchor=CENTER, image=self.image, tags="IMG")

root = Tk()
app = App(root)
app.mainloop()

所以它所做的是创建一个 canvas 和两个 scale,只要其中一个 scale 发生变化,它就会调用 def resize 从 Canvas 中清除带有标签 IMG 的项目,然后绘制一个 width 等于xscale 的值和高度等于 yscale 的标签为 IMG。当您沿着 scale 拖动 slider 时,会产生实时大小更新的错觉。

但是,我想不出一种方法来将 scale 的上限值限制为 canvas 的宽度(以像素为单位)以防止用户扩展图像在 canvas 的边界之外。

这可以类似地应用于允许您使用 slider 值实时更新您需要更新的图像的任何属性。

关于python - 如何使用 tkinter 比例修改图像?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36657126/

相关文章:

python - 查看仅当前用户对象的列表,Django REST

python - 在 Windows 中运行 python 脚本

opencv - 捕获机器可读区域的算法

Python 从返回元组追加到列表

python - 反向传播的扩展

python - 在 python 中加速 int 列表到二进制的转换

mysql - Python。我正在创建一个电话数据库并将其与 python 代码链接

python - Google Vision API 标签检测

visual-c++ - 从视频中获取帧

c# - 从指针创建 Mat,调整它的大小并将指针返回到新的 mat