2015-02-18 3 views
1

Я пытаюсь создать полосы прокрутки в окне, создаваемом OpenCv python. Я знаю, что мне нужно реализовать код для обработки процесса прокрутки/панорамирования, но я понятия не имею, с чего начать, и я везде искал. Очень важно, чтобы я создавал полосы прокрутки в окне OpenCV вместо использования какой-либо другой рамки окна графического интерфейса пользователя. Ниже приведен код, который я использую для загрузки изображения и масштабирования изображения (которое работает). Любая помощь приветствуется. И, пожалуйста, не направляйте меня в документацию opencv по созданию трекболов, я ее прочитал, и это совсем не помогает. Благодаря!Создание трекболов для прокрутки большого изображения в OpenCV Python

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

Не могли бы вы объяснить, почему вы не можете поместить изображение в окно gui? – IronManMark20

+0

Потому что я делаю обработку изображения на изображении, как получение информации о пикселях, и я потеряю эту способность, если я инкапсулирую ее в виджет или окно, предоставленное инфраструктурой GUI. – wunjo

ответ

1

Потому что я делаю обработки изображений на изображении, как получение информации пикселя и я потеряет эту способность, если я инкапсулировать его в виджете или окна, обеспечиваемые средой GUI

Это ISN Это правда. Вы всегда можете обновить изображение после обработки. Например, посмотрите here и here especially.
Эти примеры обрабатывают изображения в OpenCv и помещают их в кадр gui PyQt. Я уверен, что вы могли бы делать подобные вещи с другими фреймами 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() 

Конечно, это не то, что вы на самом деле будет писать, это абстракция идеи.

EDIT: В этом случае я бы сделать видео (см this example & here), а затем взять изображение как отдельный объект, затем сделать (опять-таки в качестве третьего объекта) с PyQt. Чтобы поймать местоположение щелчка мыши, посмотрите на this question и, наконец, ссылку на второй объект, который является изображением OpenCV.

+0

Спасибо за ввод. В настоящее время у меня есть видео-канал камеры, который снимается и обрабатывается с помощью opencv, а затем я конвертировал его в подходящую форму для pyqt, как вы уже говорили.Проблема в том, что мне нужно сделать снимок текущего кадра, отобразить снимок и щелкнуть по пикселям, чтобы получить их значения hsv. Я не пробовал держать его в том же окне pyqt; Я только что создал новое окно opencv. Я попытаюсь просмотреть снимок после его преобразования в образ pyqt и посмотреть, смогу ли я получить информацию о пикселях. Если смогу, я попробую добавить к нему полосы прокрутки. – wunjo

2

У меня была такая же потребность, поэтому сегодня я создал класс с нуля, который обрабатывает щелчки мышью, панорамирование и масштабирование в окне OpenCV. Вот как это работает:

  1. правой перетаскивание вверх или вниз, чтобы увеличить
  2. правой кнопкой мыши, чтобы отцентрировать вид на мышь
  3. перетащить х и у trackbars прокручивать
  4. при инициализации , вы можете опционально передать функцию, которая будет вызываться, когда пользователь щелкнет левой кнопкой мыши на пикселе

(Насколько я могу судить, OpenCV не может прочитать колесо мыши и не может создать вертикальную trackbar, поэтому пользовательский интерфейс немного неинтуирует но он работает.)

# -*- 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() 
+0

это действительно круто! – crld

+1

messing around с вашим кодом Я понял, что закрытие окна (а не q или ESC) оставило python, а скрипт все еще работает в фоновом режиме. если вы измените цикл while на 'while key! = ord ('q') и ключ! = 27 и cv2.getWindowProperty (window.WINDOW_NAME, 0)> = 0:' эта проблема исправлена. – crld

+1

Это работает в python3, как только утверждения печати исправлены. Кроме того, вы можете добавить поддержку цветного изображения, заменив 'self.imShape = np.array (imShape)' на 'self.imShape = np.array (imShape [0: 2])'. – Masterfool