我正在尝试在OpenCv python创建的窗口中创建滚动条。我知道我需要实现代码来处理滚动/平移过程,但我不知道从哪里开始,我到处都是。我必须在OpenCV窗口中创建滚动条而不是使用其他GUI窗口框架。下面是我用来加载图像和缩放图像的代码(有效)。任何帮助表示赞赏。请不要向我推荐有关创建轨迹栏的opencv文档,我已经阅读过它并且它根本没有帮助。谢谢!
import cv2
import cv2.cv as cv
import numpy as np
cv.NamedWindow('image', cv.CV_WINDOW_AUTOSIZE)
cv.NamedWindow('Control Window', cv.CV_WINDOW_AUTOSIZE)
print " Zoom In-Out demo "
print " Press u to zoom "
print " Press d to zoom "
img = cv2.imread('picture.jpg')
while(1):
h,w = img.shape[:2]
cv2.imshow('image',img)
k = cv2.waitKey(10)
if k==27 :
break
elif k == ord('u'): # Zoom in, make image double size
img = cv2.pyrUp(img,dstsize = (2*w,2*h))
elif k == ord('d'): # Zoom down, make image half the size
img = cv2.pyrDown(img,dstsize = (w/2,h/2))
cv2.destroyAllWindows()
答案 0 :(得分:7)
我有同样的需求,所以今天我从头创建了一个类,用于处理OpenCV窗口上的鼠标点击,平移和缩放。它的工作原理如下:
(据我所知,OpenCV无法读取鼠标滚轮,无法创建垂直轨迹栏,因此用户体验有点不直观,但有效。)
# -*- coding: utf-8 -*-
import cv2
import numpy as np
class PanZoomWindow(object):
""" Controls an OpenCV window. Registers a mouse listener so that:
1. right-dragging up/down zooms in/out
2. right-clicking re-centers
3. trackbars scroll vertically and horizontally
You can open multiple windows at once if you specify different window names.
You can pass in an onLeftClickFunction, and when the user left-clicks, this
will call onLeftClickFunction(y,x), with y,x in original image coordinates."""
def __init__(self, img, windowName = 'PanZoomWindow', onLeftClickFunction = None):
self.WINDOW_NAME = windowName
self.H_TRACKBAR_NAME = 'x'
self.V_TRACKBAR_NAME = 'y'
self.img = img
self.onLeftClickFunction = onLeftClickFunction
self.TRACKBAR_TICKS = 1000
self.panAndZoomState = PanAndZoomState(img.shape, self)
self.lButtonDownLoc = None
self.mButtonDownLoc = None
self.rButtonDownLoc = None
cv2.namedWindow(self.WINDOW_NAME, cv2.WINDOW_NORMAL)
self.redrawImage()
cv2.setMouseCallback(self.WINDOW_NAME, self.onMouse)
cv2.createTrackbar(self.H_TRACKBAR_NAME, self.WINDOW_NAME, 0, self.TRACKBAR_TICKS, self.onHTrackbarMove)
cv2.createTrackbar(self.V_TRACKBAR_NAME, self.WINDOW_NAME, 0, self.TRACKBAR_TICKS, self.onVTrackbarMove)
def onMouse(self,event, x,y,_ignore1,_ignore2):
""" Responds to mouse events within the window.
The x and y are pixel coordinates in the image currently being displayed.
If the user has zoomed in, the image being displayed is a sub-region, so you'll need to
add self.panAndZoomState.ul to get the coordinates in the full image."""
if event == cv2.EVENT_MOUSEMOVE:
return
elif event == cv2.EVENT_RBUTTONDOWN:
#record where the user started to right-drag
self.mButtonDownLoc = np.array([y,x])
elif event == cv2.EVENT_RBUTTONUP and self.mButtonDownLoc is not None:
#the user just finished right-dragging
dy = y - self.mButtonDownLoc[0]
pixelsPerDoubling = 0.2*self.panAndZoomState.shape[0] #lower = zoom more
changeFactor = (1.0+abs(dy)/pixelsPerDoubling)
changeFactor = min(max(1.0,changeFactor),5.0)
if changeFactor < 1.05:
dy = 0 #this was a click, not a draw. So don't zoom, just re-center.
if dy > 0: #moved down, so zoom out.
zoomInFactor = 1.0/changeFactor
else:
zoomInFactor = changeFactor
# print "zoomFactor:",zoomFactor
self.panAndZoomState.zoom(self.mButtonDownLoc[0], self.mButtonDownLoc[1], zoomInFactor)
elif event == cv2.EVENT_LBUTTONDOWN:
#the user pressed the left button.
coordsInDisplayedImage = np.array([y,x])
if np.any(coordsInDisplayedImage < 0) or np.any(coordsInDisplayedImage > self.panAndZoomState.shape[:2]):
print "you clicked outside the image area"
else:
print "you clicked on",coordsInDisplayedImage,"within the zoomed rectangle"
coordsInFullImage = self.panAndZoomState.ul + coordsInDisplayedImage
print "this is",coordsInFullImage,"in the actual image"
print "this pixel holds ",self.img[coordsInFullImage[0],coordsInFullImage[1]]
if self.onLeftClickFunction is not None:
self.onLeftClickFunction(coordsInFullImage[0],coordsInFullImage[1])
#you can handle other mouse click events here
def onVTrackbarMove(self,tickPosition):
self.panAndZoomState.setYFractionOffset(float(tickPosition)/self.TRACKBAR_TICKS)
def onHTrackbarMove(self,tickPosition):
self.panAndZoomState.setXFractionOffset(float(tickPosition)/self.TRACKBAR_TICKS)
def redrawImage(self):
pzs = self.panAndZoomState
cv2.imshow(self.WINDOW_NAME, self.img[pzs.ul[0]:pzs.ul[0]+pzs.shape[0], pzs.ul[1]:pzs.ul[1]+pzs.shape[1]])
class PanAndZoomState(object):
""" Tracks the currently-shown rectangle of the image.
Does the math to adjust this rectangle to pan and zoom."""
MIN_SHAPE = np.array([50,50])
def __init__(self, imShape, parentWindow):
self.ul = np.array([0,0]) #upper left of the zoomed rectangle (expressed as y,x)
self.imShape = np.array(imShape[0:2])
self.shape = self.imShape #current dimensions of rectangle
self.parentWindow = parentWindow
def zoom(self,relativeCy,relativeCx,zoomInFactor):
self.shape = (self.shape.astype(np.float)/zoomInFactor).astype(np.int)
#expands the view to a square shape if possible. (I don't know how to get the actual window aspect ratio)
self.shape[:] = np.max(self.shape)
self.shape = np.maximum(PanAndZoomState.MIN_SHAPE,self.shape) #prevent zooming in too far
c = self.ul+np.array([relativeCy,relativeCx])
self.ul = c-self.shape/2
self._fixBoundsAndDraw()
def _fixBoundsAndDraw(self):
""" Ensures we didn't scroll/zoom outside the image.
Then draws the currently-shown rectangle of the image."""
# print "in self.ul:",self.ul, "shape:",self.shape
self.ul = np.maximum(0,np.minimum(self.ul, self.imShape-self.shape))
self.shape = np.minimum(np.maximum(PanAndZoomState.MIN_SHAPE,self.shape), self.imShape-self.ul)
# print "out self.ul:",self.ul, "shape:",self.shape
yFraction = float(self.ul[0])/max(1,self.imShape[0]-self.shape[0])
xFraction = float(self.ul[1])/max(1,self.imShape[1]-self.shape[1])
cv2.setTrackbarPos(self.parentWindow.H_TRACKBAR_NAME, self.parentWindow.WINDOW_NAME,int(xFraction*self.parentWindow.TRACKBAR_TICKS))
cv2.setTrackbarPos(self.parentWindow.V_TRACKBAR_NAME, self.parentWindow.WINDOW_NAME,int(yFraction*self.parentWindow.TRACKBAR_TICKS))
self.parentWindow.redrawImage()
def setYAbsoluteOffset(self,yPixel):
self.ul[0] = min(max(0,yPixel), self.imShape[0]-self.shape[0])
self._fixBoundsAndDraw()
def setXAbsoluteOffset(self,xPixel):
self.ul[1] = min(max(0,xPixel), self.imShape[1]-self.shape[1])
self._fixBoundsAndDraw()
def setYFractionOffset(self,fraction):
""" pans so the upper-left zoomed rectange is "fraction" of the way down the image."""
self.ul[0] = int(round((self.imShape[0]-self.shape[0])*fraction))
self._fixBoundsAndDraw()
def setXFractionOffset(self,fraction):
""" pans so the upper-left zoomed rectange is "fraction" of the way right on the image."""
self.ul[1] = int(round((self.imShape[1]-self.shape[1])*fraction))
self._fixBoundsAndDraw()
if __name__ == "__main__":
infile = "./testImage.png"
myImage = cv2.imread(infile,cv2.IMREAD_ANYCOLOR)
window = PanZoomWindow(myImage, "test window")
key = -1
while key != ord('q') and key != 27: # 27 = escape key
#the OpenCV window won't display until you call cv2.waitKey()
key = cv2.waitKey(5) #User can press 'q' or ESC to exit.
cv2.destroyAllWindows()
答案 1 :(得分:1)
因为我在图像上进行图像处理就像获取像素信息一样,如果我将它封装在GUI框架提供的小部件或窗口中,我将失去这种能力
这不是真的。您可以在处理后始终更新图像。例如,查看here和here especially。
这些示例在OpenCv中处理图像并将它们放在PyQt gui框架中。我相信你可以和其他Gui框架做类似的事情(我无法为Tkinter找到任何东西)。我想我过去已经看过wxPython集成了。
制作程序时,请务必显示图像副本。这样,图像对象将继续可更改,您只需更新Gui中的图像即可。例如,这里有一些伪代码:
image=Image("myimage.png")
image.resize(100,400)
img=QImage(image)#similar to how pyqt would work
img.show()
image.invert_colors()
img=QImage(image)
img.show()
当然,这不是你真正要写的东西,它是这个想法的抽象。
编辑:在这种情况下,我会渲染视频(请参阅this example&amp; here),然后将图像作为单独的对象,然后使用pyqt渲染(再次作为第三个对象)。要捕获鼠标单击的位置,请查看this question,最后,将该点引用到第二个对象,即OpenCV图像。