如何调整显示图像的曝光(openCV / PyQT)?

huangapple go评论76阅读模式
英文:

How to adjust exposure of the displayed image (openCV / PyQT)?

问题

I use PyQT and OpenCV to get a thread from my webcam.
我使用PyQT和OpenCV从我的网络摄像头获取线程。

I use a slider to adjust the max exposure.
我使用滑块来调整最大曝光。

When I stop the thread, the last image stays displayed, but the slider doesn't adjust its exposure.
当我停止线程时,最后一幅图像仍然显示,但滑块不会调整曝光。

I guess I should connect it to a function
我猜我应该将它连接到一个函数

self.slider_expo.valueChanger['int'].connect(self.expo_value_change)

but it already works well when the thread is started. How can I make something specific to handle the last image displayed when the thread is stopped?
但是当线程启动时,它已经很好地工作了。当线程停止时,我如何制作一些特定的东西来处理最后显示的图像?

It's sometimes difficult to find the good value on a changing image; so I want to stop the thread to "pause" on a picture, adjust my value and then start the thread again.
在变化的图像上找到合适的值有时很困难;因此,我想停止线程以“暂停”在一张图片上,调整我的值,然后再次启动线程。

英文:

I use PyQT and OpenCV to get a thread from my webcam.
I use a slider to adjust the max exposure.
When I stop the thread, the last image stays displayed, but the slider doesn't adjust its exposure.
I guess I should connect it to a function

> self.slider_expo.valueChanger['int'].connect(self.expo_value_change)

but it already works well when the thread is started. How can I make something specific to handle the last image displayed when the thread is stopped?
It's sometimes difficult to find the good value on a changing image; so I want to stop the thread to "pause" on a picture, adjust my value and then start the thread again.

import sys
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtMultimedia import QCameraInfo
import cv2
from PyQt5.QtWidgets import QApplication, QMainWindow, QDesktopWidget, QStatusBar, QLabel
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtGui import QPixmap
import numpy as np
from Process import Ui_MainWindow
import skimage.exposure

class VideoThread(QThread):
    change_pixmap_signal = pyqtSignal(np.ndarray)

    def __init__(self):
        super().__init__()
        self._run_flag = True

    def run(self):
        # capture from webcam
        self._run_flag = True
        self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
        while self._run_flag:
            ret, cv_img = self.cap.read()
            if ret:
                self.change_pixmap_signal.emit(cv_img)
        # shut down capture system
        self.cap.release()

    def stop(self):
        """Sets run flag to False and waits for thread to finish"""
        self._run_flag = False


class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.available_cameras = QCameraInfo.availableCameras()  # Getting available cameras

        cent = QDesktopWidget().availableGeometry().center()  # Finds the center of the screen
        self.setStyleSheet("background-color: white;")
        self.resize(1400, 800)
        self.frameGeometry().moveCenter(cent)
        self.setWindowTitle('S L A I T')
        self.initWindow()

########################################################################################################################
#                                                   Windows                                                            #
########################################################################################################################
    def initWindow(self):
        # create the video capture thread
        self.thread = VideoThread()

        # Button to start video
        self.ss_video = QtWidgets.QPushButton(self)
        self.ss_video.setText('Start video')
        self.ss_video.move(769, 100)
        self.ss_video.resize(300, 100)
        self.ss_video.clicked.connect(self.ClickStartVideo)

        #Slider to change max exposure
        self.slider_expo = QtWidgets.QSlider(self)
        self.slider_expo.setMinimum(52)
        self.slider_expo.setMaximum(255)
        self.slider_expo.setProperty("value",99)
        self.slider_expo.resize(10, 501)
        self.slider_expo.move(700, 0)

        # Status bar
        self.status = QStatusBar()
        self.status.setStyleSheet("background : lightblue;")  # Setting style sheet to the status bar
        self.setStatusBar(self.status)  # Adding status bar to the main window
        self.status.showMessage('Ready to start')

        self.image_label = QLabel(self)
        self.disply_width = 669
        self.display_height = 501
        self.image_label.resize(self.disply_width, self.display_height)
        self.image_label.setStyleSheet("background : black;")
        self.image_label.move(0, 0)

########################################################################################################################
#                                                   Buttons                                                            #
########################################################################################################################
    # Activates when Start/Stop video button is clicked to Start (ss_video
    def ClickStartVideo(self):
        # Change label color to light blue
        self.ss_video.clicked.disconnect(self.ClickStartVideo)
        self.status.showMessage('Video Running...')
        # Change button to stop
        self.ss_video.setText('Stop video')
        self.thread = VideoThread()
        self.thread.change_pixmap_signal.connect(self.update_image)

        # start the thread
        self.thread.start()
        self.ss_video.clicked.connect(self.thread.stop)  # Stop the video if button clicked
        self.ss_video.clicked.connect(self.ClickStopVideo)

    # Activates when Start/Stop video button is clicked to Stop (ss_video)
    def ClickStopVideo(self):
        self.thread.change_pixmap_signal.disconnect()
        self.ss_video.setText('Start video')
        self.status.showMessage('Ready to start')
        self.ss_video.clicked.disconnect(self.ClickStopVideo)
        self.ss_video.clicked.disconnect(self.thread.stop)
        self.ss_video.clicked.connect(self.ClickStartVideo)

########################################################################################################################
#                                                   Actions                                                            #
########################################################################################################################

    def update_image(self, cv_img):
        """Updates the image_label with a new opencv image"""
        min_expo = 50
        max_expo = self.slider_expo.value()
        img_expo = skimage.exposure.rescale_intensity(cv_img, in_range=(min_expo, max_expo),
                                                      out_range=(0, 255)).astype(np.uint8)
        qt_img = self.convert_cv_qt(img_expo)
        self.image_label.setPixmap(qt_img)

    def convert_cv_qt(self, cv_img):
        """Convert from an opencv image to QPixmap"""
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
        p = convert_to_Qt_format.scaled(self.disply_width, self.display_height, Qt.KeepAspectRatio)
        #p = convert_to_Qt_format.scaled(801, 801, Qt.KeepAspectRatio)
        return QPixmap.fromImage(p)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec())

答案1

得分: 0

以下是您要翻译的内容:

Your application updates image only when update_image is called. This method is only called when change_pixmap_signal signal is sent by the VideoThread. To update the image when the VideoThread is paused or stopped, you will have to first connect the slider with a new slot as you guessed. Next, you will need to save the last image received in update_image as a class member variable.

Also, make sure the slider value changed signal is not connected to a slot to update the image when the video is playing. Otherwise, you will have two different update methods running at the same time.

英文:

Your application updates image only when update_image is called. This method is only called when change_pixmap_signal signal is send by the VideoThread. To update image when the VideoThread is paused or stopped, you will have to first connect slider with a new slot as you guessed. Next, you will need to save the last image received in update_image as a class member variable.

Also, make sure the slider value changed signal is not connected to a slot to update image when video is playing. Otherwise, you will have two different update methods running at the same time.

import sys
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtMultimedia import QCameraInfo
import cv2
from PyQt5.QtWidgets import QApplication, QMainWindow, QDesktopWidget, QStatusBar, QLabel
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtGui import QPixmap
import numpy as np
import skimage.exposure


class VideoThread(QThread):
    change_pixmap_signal = pyqtSignal(np.ndarray)

    def __init__(self):
        super().__init__()

    def run(self):
        # capture from webcam
        self._run_flag = True
        self.cap = cv2.VideoCapture(2)
        while self._run_flag:
            ret, cv_img = self.cap.read()
            if ret:
                self.change_pixmap_signal.emit(cv_img)
        # shut down capture system
        self.cap.release()

    def stop(self):
        """Sets run flag to False and waits for thread to finish"""
        self._run_flag = False


class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.available_cameras = QCameraInfo.availableCameras()  # Getting available cameras

        cent = QDesktopWidget().availableGeometry().center()  # Finds the center of the screen
        self.setStyleSheet("background-color: white;")
        self.resize(1400, 800)
        self.frameGeometry().moveCenter(cent)
        self.setWindowTitle('S L A I T')
        self.initWindow()
        self.cv_img = None

    ########################################################################################################################
    #                                                   Windows                                                            #
    ########################################################################################################################
    def initWindow(self):
        # create the video capture thread
        self.thread = VideoThread()

        # Button to start video
        self.ss_video = QtWidgets.QPushButton(self)
        self.ss_video.setText('Start video')
        self.ss_video.move(769, 100)
        self.ss_video.resize(300, 100)
        self.ss_video.clicked.connect(self.ClickStartVideo)

        # Slider to change max exposure
        self.slider_expo = QtWidgets.QSlider(self)
        self.max_expo = 99
        self.slider_expo.setMinimum(52)
        self.slider_expo.setMaximum(255)
        self.slider_expo.setProperty("value", 99)
        self.slider_expo.resize(10, 501)
        self.slider_expo.move(700, 0)

        # Status bar
        self.status = QStatusBar()
        self.status.setStyleSheet("background : lightblue;")  # Setting style sheet to the status bar
        self.setStatusBar(self.status)  # Adding status bar to the main window
        self.status.showMessage('Ready to start')

        self.image_label = QLabel(self)
        self.disply_width = 669
        self.display_height = 501
        self.image_label.resize(self.disply_width, self.display_height)
        self.image_label.setStyleSheet("background : black;")
        self.image_label.move(0, 0)

    ########################################################################################################################
    #                                                   Buttons                                                            #
    ########################################################################################################################
    # Activates when Start/Stop video button is clicked to Start (ss_video
    def ClickStartVideo(self):
        # Change label color to light blue
        self.ss_video.clicked.disconnect(self.ClickStartVideo)
        self.status.showMessage('Video Running...')
        # Change button to stop
        self.ss_video.setText('Stop video')
        self.thread = VideoThread()
        self.thread.change_pixmap_signal.connect(self.update_image)

        # start the thread
        self.thread.start()
        self.ss_video.clicked.connect(self.thread.stop)  # Stop the video if button clicked
        self.ss_video.clicked.connect(self.ClickStopVideo)

        # Disconnect updating image when slider is changed.
        if self.cv_img is not None:
            self.slider_expo.valueChanged.disconnect(self.update_image_when_video_paused)

    # Activates when Start/Stop video button is clicked to Stop (ss_video)
    def ClickStopVideo(self):
        self.thread.change_pixmap_signal.disconnect()
        self.ss_video.setText('Start video')
        self.status.showMessage('Ready to start')
        self.ss_video.clicked.disconnect(self.ClickStopVideo)
        self.ss_video.clicked.disconnect(self.thread.stop)
        self.ss_video.clicked.connect(self.ClickStartVideo)

        # Connect the slider signal to update image
        self.slider_expo.valueChanged.connect(self.update_image_when_video_paused)

    ########################################################################################################################
    #                                                   Actions                                                            #
    ########################################################################################################################

    def render_processed_image(self, cv_img, min_expo=50, max_expo=255):
        if min_expo != 50:
            print(min_expo)
        if max_expo != self.max_expo:
            print(f"max_expo changed to {max_expo}")
            self.max_expo = max_expo
        img_expo = skimage.exposure.rescale_intensity(cv_img, in_range=(min_expo, max_expo),
                                                      out_range=(0, 255)).astype(np.uint8)
        qt_img = self.convert_cv_qt(img_expo)
        self.image_label.setPixmap(qt_img)

    def update_image(self, cv_img):
        """Updates the image_label with a new opencv image"""
        self.cv_img = cv_img
        self.render_processed_image(self.cv_img, max_expo=self.slider_expo.value())

    def update_image_when_video_paused(self):
        self.render_processed_image(self.cv_img, max_expo=self.slider_expo.value())

    def convert_cv_qt(self, cv_img):
        """Convert from an opencv image to QPixmap"""
        rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
        p = convert_to_Qt_format.scaled(self.disply_width, self.display_height, Qt.KeepAspectRatio)
        # p = convert_to_Qt_format.scaled(801, 801, Qt.KeepAspectRatio)
        return QPixmap.fromImage(p)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec())

huangapple
  • 本文由 发表于 2023年6月13日 17:32:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76463500.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定