Pyqtgraph plotwidget displays part of the image outside the plotwidget window, image captured by opencv from webcam

463 views
Skip to first unread message

Pramod Butte

unread,
May 27, 2018, 10:31:11 PM5/27/18
to pyqtgraph
Hello,

I am writing a program to capture image using a webcam, can find the largest contour and plot it to an app using pyqtgraph plotwidget . 

I am capturing a frame using opencv from the webcam, attached on my xps 13. Then I am trying to display it with embedded qt ui using plotwidget (I have promoted in the designer to plotwidget I tried using ImageView, but it gives me error when I try to to setUI). 

My current problem is and I have been searching the solution for 3 days now is that the image captured is displayed outside the bounds of plotwidget. all I can see is a cropped image in the plotwidget. 
I tried setting up a separate viewbox and add it as a setcentralwidget() to plotwidget. I tried setting properties of plotwidget directly such as enablerange. etc. I am at my wits end and looking for anyone to help.

thank you,
Pramod

Code :

1. Class for webcam capture

class WebcamWorker(QRunnable):

def __init__(self, cam,width,height,fps):
super(WebcamWorker, self).__init__()
# Store constructor arguments (re-used for processing)
self.cam = cam
self.width = width
self.height = height
self.fps = fps
self.signals = WebCamSignal()

self.capture = cv2.VideoCapture(self.cam)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
self.capture.set(cv2.CAP_PROP_FPS, self.fps)

@pyqtSlot()
def run(self):
#etval, img = self.capture.read(0)
while(running):

retval, img = self.capture.read(0)
cv2.waitKey(30)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
ret, thresh = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 100, 255, cv2.THRESH_BINARY_INV)

#blur = cv2.GaussianBlur(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), (5, 5), 0)
#ret3, thresh = c(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
x=[]
y=[]
w=[]
h=[]
area_c=[]
if len(contours) >0:
for c in contours:
#cnt = contours[0]
x_tmp, y_tmp, w_tmp, h_tmp = cv2.boundingRect(c)
x.append(x_tmp)
y.append(y_tmp)
w.append(w_tmp)
h.append(h_tmp)
area_c.append(cv2.contourArea(c))
#if len(self.area_c) > 0:
index = area_c.index(max(area_c))
#cv2.rectangle(img, (x_tmp, y_tmp), (x_tmp + w_tmp, y_tmp + h_tmp), (255, 153, 0), 1)
cv2.rectangle(img, (x[index], y[index]), (x[index] + w[index], y[index] + h[index]), (50, 255, 255), 1)

X0 = (x[index]/self.width)*100
Y0 = (y[index]/self.height)*100

#x,y corners of the rectangle
cv2.putText(img,f'{X0:.2f}% , {Y0:.2f}%',(x[index]-50,y[index]),cv2.FONT_HERSHEY_SIMPLEX,0.4,(255,255,255),1,cv2.LINE_AA)
#height of rectangle (height)
cv2.putText(img, str(w[index]), (x[index]+(int(w[index]/2)), y[index]),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1, cv2.LINE_AA)

#width of the rectangle
cv2.putText(img, str(h[index]), (x[index]-50, y[index]+(int(h[index]/2))),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1, cv2.LINE_AA)

(h_max, w_max) = img.shape[:2]
# calculate the center of the image
center = (w_max / 2, h_max / 2)
# angle90 = 90
angle180 = 180
angle270 = 270
scale = 1.0
M = cv2.getRotationMatrix2D(center, 270, scale)
img = cv2.warpAffine(img, M, (h_max, w_max))
self.signals.captured.emit(img)

2. create instance of class:
running = True
self.webcam = WebcamWorker(0,640,480,30)
self.webcam.signals.captured.connect(self.run_webcam)
self.threadpool.start(self.webcam)

3. main ui from designer .ui file
qtCreatorFile = "TRFS27B.ui"  # Enter file here.
Ui_MainWindow, QtBaseClass = pg.Qt.loadUiType(qtCreatorFile)

self.ui = Ui_MainWindow()
self.ui.setupUi(self)


self.mainWindowvb = pg.ViewBox()
self.mainWindowvb.setAspectLocked()
self.ui.mainWindow.showAxis('left',False)
self.ui.mainWindow.showAxis('bottom', False)
self.ui.mainWindow.showGrid(x=True, y=True, alpha=1)
self.ui.mainWindow.setCentralWidget(self.mainWindowvb)
self.pc = pg.PlotCurveItem()
#self.imv = pg.ImageView()
self.im1 = pg.ImageItem()

self.window_width, self.window_height = self.mainWindowvb.viewPixelSize()
self.mainWindowvb.enableAutoRange(enable= True)
self.mainWindowvb.addItem(self.im1)

4. display image to plotwidget

def run_webcam(self, img ):
self.im1.setImage(img, border='w')
Screenshot from 2018-05-27 19-15-30.png

Patrick

unread,
May 28, 2018, 10:23:37 PM5/28/18
to pyqtgraph
Hi,

I'm assuming you're using pyqtgraph to draw the image because you'd like to use other plot/analysis features (otherwise there's simpler ways of just drawing an image to a widget!). I started by promoting a QGraphicsView to a GraphicsLayoutWidget in Qt Designer.

Then something like this to set it up:
self.plot = self.plot_graphicsLayoutWidget.addPlot()
self.plot.showGrid(x=False, y=False)
self.plot.setLabels(bottom="Distance (mm)")
self.plot.enableAutoRange(pg.ViewBox.XYAxes)
self.im1 = pg.ImageItem(np.zeros((640, 480)))
self.plot.addItem(self.im1)
# Scale and translate if you'd like to turn pixels into some other units
self.im1.translate(x_distance_offset, 0)
self.im1.scale(x_distance_per_pixel, 1.0)

Then update image on the signal as you have already done.
If you need to poke at the ViewBox for more control over layout, you can get a reference to it from plot.getViewBox().
Hope that helps!

Patrick

Pramod Butte

unread,
May 31, 2018, 5:09:37 PM5/31/18
to pyqt...@googlegroups.com
thank you I will try it today!

--
You received this message because you are subscribed to a topic in the Google Groups "pyqtgraph" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/pyqtgraph/QbQr5mxnvHc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to pyqtgraph+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pyqtgraph/2cf46e41-4922-40f0-b921-151491c3dd86%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Pramod Butte

unread,
May 31, 2018, 5:13:01 PM5/31/18
to pyqt...@googlegroups.com
I am rotating the image in opencv before plotting it in pyqtgraph. the reason for using the plotwidget with VB is eventually i would like to overlay grid on it. I tried getting the getViewbox() and set the image to it. but it seems the image is being shown outside the viewbox and if I measure it correctly it is being set to the 0,0 of the widget. 

Have you ever used the Imageview instead of plotwidget or graphicsview?

Pramod

Pramod

Patrick

unread,
May 31, 2018, 8:21:43 PM5/31/18
to pyqtgraph
You shouldn't need to touch the ViewBox to update the image - just update the data for the ImageItem on the webcam event as you have done in step 4 of your first post:

def run_webcam(self, img ):
   
self.im1.setImage(img)

By default the ImageItem will plot out the image on the plot axes starting at 0,0, with units in pixels, and values increasing up on the y-axis and to the right on the x-axis. The scale and translate functions will adjust this if you'd like to convert pixels to different units for the axes labels.

To plot so the units increase downwards on the y axis, use self.plot.invertY(True)

The image data is interpreted in column-major order by default. To change that behaviour pg.setConfigOptions(imageAxisOrder='row-major') -- which is the same as transposing the image numpy array.

With combinations of those two options you may not need to do any rotating/flipping of your webcam image data.

Patrick

Pramod Butte

unread,
Jun 7, 2018, 7:47:03 AM6/7/18
to pyqt...@googlegroups.com
Thanks Patrick,

1. The image captured by opencv is always same aspect ratio no matter what parameters I give.

2. The image needs to be rotated as you mentioned and I realized (I miss be doing something wrong) that when I rotate the image on it's center (I will now try to see if I can set the configuration) the new "center" is shifted. Anyways I hacked it for now by moving the center to the right.

Thanks


Reply all
Reply to author
Forward
0 new messages