Monday, March 22, 2021

min. version of RPi/Python Code to control Camera Module with preview on local HDMI

It's a minimum version of my another exercise "RPi/Python Code to control Camera Module with preview on local HDMI"; with minimum functions preview and capture only, using PyQt5 GUI, fixed resolution 240x240, and display the captured image on GUI. You can also choice save file name; image.jpg or img_<timestamp>.jpg, under Desktop folder. It's used to prepare images for my coming exercise "Raspberry Pi/Python send image to ESP32/MicroPython via WiFi TCP socket".

In my usage scenario: The Raspberry Pi 4B/8G is installed with HQ Camera Module (mount with manual focus lens) and a 4 inch HDMI IPS Touch Display. Remote control with Android with xrdp/Microsoft Remote Desktop. Such that I can control camera on remote Android device, adjust focus/aperture on the lens, and check the effect on local HDMI preview at real-time.


Python3 code, qCam240_20210323.py
import sys
import picamera
from pkg_resources import require
import time
import picamera

from PyQt5.QtWidgets import (QApplication, QWidget,
                             QPushButton, QLabel, QRadioButton,
                             QMessageBox, QHBoxLayout, QVBoxLayout)
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.Qt import qRed, qGreen, qBlue
from signal import signal, SIGINT

print(sys.version)
print(require('picamera'))

rpi_icon = 'rpi_icon_240.png'

class AppWindow(QWidget):
    
    camPreviewState = False  #not in Preview
    
    def __init__(self):
        super().__init__()

        self.camera = picamera.PiCamera()
        self.camera.resolution = (240, 240)
        
        lbSysInfo = QLabel('Python:\n' + sys.version)
        lbPicameraInfo = QLabel(str(require('picamera')))
        vboxInfo = QVBoxLayout()
        vboxInfo.addWidget(lbSysInfo)
        vboxInfo.addWidget(lbPicameraInfo)
        
        #setup UI
        btnPreview = QPushButton("Start Preview", self)
        btnPreview.clicked.connect(self.evBtnPreviewClicked)
        btnCapture = QPushButton("Capture", self)
        btnCapture.clicked.connect(self.evBtnCaptureClicked)
        
        lbFileName = QLabel('save as (Desktop/):')
        self.rbtnImage = QRadioButton('image.jpg')
        self.rbtnImage.setChecked(True)
        self.rbtnStamp = QRadioButton('img_<timestamp>.jpg')
        
        vboxCamControl = QVBoxLayout()
        vboxCamControl.addWidget(btnPreview)
        vboxCamControl.addWidget(btnCapture)
        vboxCamControl.addWidget(lbFileName)
        vboxCamControl.addWidget(self.rbtnImage)
        vboxCamControl.addWidget(self.rbtnStamp)
        vboxCamControl.addStretch()
        
        self.lbImg = QLabel(self)
        self.lbImg.resize(240, 240)
        self.lbImg.setStyleSheet("border: 1px solid black;")
        
        try:
            with open(rpi_icon):
                pixmap = QPixmap(rpi_icon)
                self.lbImg.setPixmap(pixmap)
        except FileNotFoundError:
            print('File Not Found Error')
        
        hboxCam = QHBoxLayout()
        hboxCam.addWidget(self.lbImg)
        hboxCam.addLayout(vboxCamControl)

        self.lbPath = QLabel(self)

        vboxMain = QVBoxLayout()
        vboxMain.addLayout(vboxInfo)
        vboxMain.addLayout(hboxCam)
        vboxMain.addWidget(self.lbPath)
        vboxMain.addStretch()
        self.setLayout(vboxMain)
        
        self.setGeometry(100, 100, 500,400)
        self.show()

    def evBtnPreviewClicked(self):
        if self.camPreviewState:
            print('Stop Preview')
            self.camera.stop_preview()
            self.sender().setText('Start Preview')
            self.camPreviewState = False
        else:
            print('Start Preview')
            self.camera.start_preview()
            self.sender().setText('Stop Preview')
            self.camPreviewState = True
        
    def evBtnCaptureClicked(self):
        print('evBtnCaptureClicked()')
        print("Capture")
        
        if self.rbtnImage.isChecked():
            targetPath="/home/pi/Desktop/image.jpg" 
        else:
            timeStamp = time.strftime("%Y%m%d-%H%M%S")
            targetPath="/home/pi/Desktop/img_"+timeStamp+".jpg"
        
        print(targetPath)
        
        self.camera.capture(targetPath)
        self.lbPath.setText(targetPath)
        
        try:
            with open(targetPath):
                pixmap = QPixmap(targetPath)
                self.lbImg.setPixmap(pixmap)
                
                #as a exercise, get some info from pixmap
                print('\npixmap:')
                print(pixmap)
                print(type(pixmap))
                print(str(pixmap.width()) + " : " + str(pixmap.height()))
                print()
                
                print('convert to Image')
                qim = pixmap.toImage()
                print(qim)
                print(type(qim))
                print()
                
                print('read a pixel from image')
                qrgb = qim.pixel(0, 0)
                print(hex(qrgb))
                print(type(qrgb))
                
                r, g, b = qRed(qrgb), qGreen(qrgb), qBlue(qrgb) 
                print([hex(r), hex(g), hex(b)])
                print()

        except FileNotFoundError:
            print('File Not Found Error')
        
    def closeEvent(self, event):
        confirmClose = QMessageBox.question(self,
                                            "Quit App?",
                                            "Confirm to Quit?",
                                            QMessageBox.No | QMessageBox.Yes,
                                            QMessageBox.Yes)
        if confirmClose == QMessageBox.Yes:
            print('Confirmed Close')
            self.camera.close()
            event.accept()
        else:
            event.ignore()
        
if __name__ == '__main__':
    print('run __main__')
    app = QApplication(sys.argv)
    window = AppWindow()
    sys.exit(app.exec_())

print("- bye -")

Download the thumbnail image, save as "rpi_icon_240.png" in the same folder. Used as a default image only.



No comments: