Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make q_picamera2 and q_gl_picamera2 dynamic size #62

Closed
jlprojects opened this issue Apr 10, 2022 · 4 comments
Closed

Make q_picamera2 and q_gl_picamera2 dynamic size #62

jlprojects opened this issue Apr 10, 2022 · 4 comments

Comments

@jlprojects
Copy link
Contributor

jlprojects commented Apr 10, 2022

I think the camera preview in the q_picamera2 and q_gl_picamera2 should be made (optionally) to automatically fill the size of its container. This problem can be seen in examples/app_capture.py - resizing or maximising the window doesn't look very good. The preview in many application cases I'd expect will need to fit the maximum space available.

My project is a cine film scanner using the HQ camera. It's original control UI was written using picamera and tkinter which sort of worked, but very clunky. I decided to start again using picamera2 and PyQt5, so I'm fumbling to implement a new GUI.
The q_picamera2 and q_gl_picamera2 widget looks ideal for showing a live view for focussing and aligning the camera, and indeed I'm starting by building on app_capture.py for a live preview and basic camera settings.

I altered q_picamera2.py to implement a dynamic resize which seems to work - patch below. It implements a resize event, decouples the camera image size from the label size, centres the camera image in the label. A layout control fills the label to the size of the parent widget. The overlay is handled and resized as necessary. It can be tested in examples/app_capture.py by replacing theq_glpicamera2.py import to q_picamera2 and the qpicamera2 declaration to qpicamera2 = QPicamera2(picam2,resizeable=True)

I'm sure it can be optimised. Similar functionality should exist on the GL preview widget but I got lost trying to understand the GL code. Just getting up to speed with basic PyQt at the moment.

--- q_picamera2.py.orig	2022-04-10 09:53:31.931882445 +0100
+++ q_picamera2.py	2022-04-10 11:10:19.290102418 +0100
@@ -1,17 +1,29 @@
 from PyQt5 import QtCore, QtGui, QtWidgets
 from PyQt5.QtCore import pyqtSlot, QSocketNotifier
-from PyQt5.QtWidgets import QWidget, QApplication, QLabel
+from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QVBoxLayout
 from PIL import Image
 from PIL.ImageQt import ImageQt
 import numpy as np
 
 
 class QPicamera2(QWidget):
-    def __init__(self, picam2, parent=None, width=640, height=480):
+    def __init__(self, picam2, parent=None, width=640, height=480, resizeable=False):
         super().__init__(parent=parent)
         self.picamera2 = picam2
+        self.image_ratio = height / width
+        self.img_size = QtCore.QSize(width, height)
         self.label = QLabel(self)
-        self.label.resize(width, height)
+        self.label.setAlignment(QtCore.Qt.AlignCenter)
+        self.label.setMinimumWidth(80)
+        self.label.setMinimumHeight(60)
+        self.resizeable = resizeable
+        if resizeable:
+            self.vbox = QVBoxLayout()
+            self.vbox.addWidget(self.label)
+            self.setLayout(self.vbox)
+        else:
+            self.label.resize(width, height)
+        self.overlay_orig = None
         self.overlay = None
         self.painter = QtGui.QPainter()
         self.camera_notifier = QSocketNotifier(self.picamera2.camera_manager.efd,
@@ -19,17 +31,31 @@
                                                self)
         self.camera_notifier.activated.connect(self.handle_requests)
 
+    def resizeEvent(self, event):
+        if not self.resizeable: return
+        # Keep aspect ratio
+        size = event.size()
+        width,height = size.width(),size.height()
+        new_width, new_height = width, int(width*self.image_ratio)
+        if new_height > height:
+            # Fit to height
+            new_width, new_height = int(height/self.image_ratio), height
+        self.img_size = QtCore.QSize(new_width,new_height)
+        if self.overlay is not None:
+            # Resize overlay
+            self.overlay = self.overlay_orig.scaled(self.img_size)
+        
     def set_overlay(self, overlay):
         if overlay is not None:
             # Better to resize the overlay here rather than in the rendering loop.
-            orig = overlay
             overlay = np.ascontiguousarray(overlay)
             shape = overlay.shape
-            size = self.label.size()
-            if orig is overlay and shape[1] == size.width() and shape[0] == size.height():
+            size = self.img_size
+            if self.overlay_orig is overlay and shape[1] == size.width() and shape[0] == size.height():
                 # We must be sure to copy the data even when no one else does!
                 overlay = overlay.copy()
             overlay = QtGui.QImage(overlay.data, shape[1], shape[0], QtGui.QImage.Format_RGBA8888)
+            self.overlay_orig = overlay.copy()
             if overlay.size() != self.label.size():
                 overlay = overlay.scaled(self.label.size())
 
@@ -43,7 +69,7 @@
 
         if self.picamera2.display_stream_name is not None:
             # This all seems horribly expensive. Pull request welcome if you know a better way!
-            size = self.label.size()
+            size = self.img_size
             img = request.make_image(self.picamera2.display_stream_name, size.width(), size.height())
             qim = ImageQt(img)
             self.painter.begin(qim)
@davidplowman
Copy link
Collaborator

Hi, thank you for sending this. Would you be able to turn this into a pull request, that way it gets some automated testing and it's convenient for folks to pull your branch if they want to try it. Thanks! (I'm away for a few days but will get back to it after that.)

@jlprojects
Copy link
Contributor Author

Hi, I'll have a go at doing a pull request today.

@jlprojects
Copy link
Contributor Author

While reimplementing my app and learning a little more about PyQt5, it occurred to me it that QPicamera2 might be better implemented as a QGraphicsView object. The preview image stored as a pixmap in the scene. Qt then handles all the view scaling automatically. Will experiment and report back...

@jlprojects
Copy link
Contributor Author

Looks like this is fixed now. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants