Module lib.video.video

Class to manage the camera of the ESP32CAM. This requires the modified firmware. I added in the firmware the possibility of detecting movements, as well as a lot of adjustment on the camera, not available on the other firmware that manages the esp32cam.

Expand source code
# Distributed under Pycameresp License
# Copyright (c) 2023 Remi BERTHOLET
# pylint:disable=consider-using-f-string
""" Class to manage the camera of the ESP32CAM.
This requires the modified firmware.
I added in the firmware the possibility of detecting movements,
as well as a lot of adjustment on the camera, not available on the other firmware that manages the esp32cam.
"""
# pylint: disable=multiple-statements
import time
import uasyncio
import os
import tools.info
import tools.system
import tools.jsonconfig
import tools.logger
if tools.info.iscamera():
        import camera

class CameraConfig(tools.jsonconfig.JsonConfig):
        """ Class that collects the camera rendering configuration """
        def __init__(self):
                """ Constructor """
                tools.jsonconfig.JsonConfig.__init__(self)
                self.activated  = True
                self.framesize  = b"640x480"
                self.pixformat  = b"JPEG"
                self.quality    = 25
                self.brightness = 0
                self.contrast   = 0
                self.saturation = 0
                self.hmirror    = False
                self.vflip      = False
                self.flash_level = 0

class Reservation:
        """ Manage the camera reservation """
        def __init__(self):
                """ Constructor """
                self.identifier = None
                self.count = 0
                self.lock = uasyncio.Lock()
                self.suspended = 0

        async def reserve(self, object_, timeout=0, suspension=None):
                """ Wait the ressource and reserve """
                result = False
                # Wait
                while True:
                        result = await self.acquire(object_, suspension)
                        if result:
                                break
                        timeout -= 1
                        if timeout < 0:
                                break
                        await uasyncio.sleep_ms(1000)
                return result

        async def acquire(self, object_, suspension=None):
                """ Reserve the camera, is used to stream the output of the camera
                to the web page. It stop the motion detection during this phase """
                result = False
                await self.lock.acquire()
                identifier = id(object_)
                try:
                        # If not reserved
                        if self.identifier is None:
                                # If suspension not required
                                if suspension is None:
                                        # If previous suspension ended
                                        if self.suspended <= 0:
                                                # Reserve
                                                self.identifier = identifier
                                                self.count = 1
                                                self.suspended = 0
                                                result = True
                                        else:
                                                # Decrease suspension counter
                                                self.suspended -= 1
                                else:
                                        # Reserve
                                        self.identifier = identifier
                                        self.count = 1
                                        self.suspended = suspension
                                        result = True
                        # If already reserved by the current object_
                        elif self.identifier == identifier:
                                # Increase reservation counter
                                self.count += 1
                                self.suspended = suspension
                                result = True
                finally:
                        self.lock.release()
                return result

        async def unreserve(self, object_):
                """ Unreserve the camera """
                result = False
                await self.lock.acquire()
                identifier = id(object_)
                try:
                        if self.identifier == identifier:
                                if self.count <= 1:
                                        self.count = 0
                                        self.identifier = None
                                else:
                                        self.count -= 1
                                result = True
                finally:
                        self.lock.release()
                return result

class Camera:
        """ Singleton class to manage the camera """
        reservation = Reservation()
        opened = False
        lock = uasyncio.Lock()
        modified = [False]
        success = [0]
        failed  = [0]
        new_failed = [0]
        config = None
        flash_enabled = [True]
        aquisition = [False]

        @staticmethod
        def gpio_config(**kwargs):
                """ Configure the structure for camera initialization. It must be done before the first call of Camera.open.
                        The defaults values are for ESP32CAM.
                        - pin_pwdn           : GPIO pin for camera power down line
                        - pin_reset          : GPIO pin for camera pin_reset line
                        - pin_xclk           : GPIO pin for camera XCLK line
                        - pin_sscb_sda       : GPIO pin for camera SDA line (SIOD)
                        - pin_sscb_scl       : GPIO pin for camera SCL line (SIOC)
                        - pin_d7             : GPIO pin for camera D7 or Y9 line
                        - pin_d6             : GPIO pin for camera D6 or Y8 line
                        - pin_d5             : GPIO pin for camera D5 or Y7 line
                        - pin_d4             : GPIO pin for camera D4 or Y6 line
                        - pin_d3             : GPIO pin for camera D3 or Y5 line
                        - pin_d2             : GPIO pin for camera D2 or Y4 line
                        - pin_d1             : GPIO pin for camera D1 or Y3 line
                        - pin_d0             : GPIO pin for camera D0 or Y2 line
                        - pin_vsync          : GPIO pin for camera VSYNC line
                        - pin_href           : GPIO pin for camera HREF line
                        - pin_pclk           : GPIO pin for camera PCLK line
                        - xclk_freq_hz       : Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental)
                        - ledc_timer         : LEDC timer to be used for generating XCLK
                        - ledc_channel       : LEDC channel to be used for generating XCLK
                        - pixel_format       : Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG
                        - frame_size         : Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
                        - jpeg_quality       : Quality of JPEG output. 0-63 lower means higher quality
                        - fb_count           : Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)
                        - flash_led          : GPIO pin for flash led or 0 to disable 
                        - grab_mode          : 0=CAMERA_GRAB_WHEN_EMPTY, 1=CAMERA_GRAB_LATEST
                        - fb_location        : 0=CAMERA_FB_IN_PSRAM, 1=CAMERA_FB_IN_DRAM
"""
                Camera.get_config()
                if Camera.is_activated():
                        camera.configure(**kwargs)

        @staticmethod
        def open():
                """ Open the camera """
                Camera.get_config()
                if Camera.is_activated():
                        result = True
                        if Camera.opened is False:
                                for i in range(10):
                                        res = camera.init()
                                        if res is False:
                                                # print("Camera not initialized")
                                                camera.deinit()
                                                time.sleep(0.5)
                                        else:
                                                break
                                else:
                                        result = False

                                if result:
                                        # Photo on 800x600, motion detection / 8 (100x75), each square detection 8x8 (12.5 x 9.375)
                                        Camera.opened = True
                                        Camera.aquisition[0] = False
                else:
                        result = False
                return result

        @staticmethod
        def get_stat():
                """ Statistic """
                return Camera.success[0], Camera.failed[0]

        @staticmethod
        def reset_stat():
                """ Reset statistic """
                Camera.success[0] = 0
                Camera.failed [0] = 0
                Camera.new_failed[0] = 0

        @staticmethod
        def close():
                """ Close the camera """
                if Camera.opened is True:
                        camera.deinit()
                        Camera.opened = False

        @staticmethod
        def reset():
                """ Reset the camera """
                if Camera.opened is True:
                        camera.reset()

        @staticmethod
        def is_opened():
                """ Indicates if the camera opened """
                return Camera.opened

        @staticmethod
        def capture():
                """ Capture an image on the camera """
                Camera.aquisition[0] = True
                return Camera.retry(camera.capture)

        @staticmethod
        def motion():
                """ Get the motion informations.
                This contains a jpeg image, with matrices of the different color RGB """
                Camera.aquisition[0] = True
                return Camera.retry(camera.motion)

        @staticmethod
        def flash(level=0):
                """ Start or stop the flash """
                if Camera.flash_enabled[0]:
                        camera.flash(level)

        @staticmethod
        def set_flash(state):
                """ On the esp32cam, we use the output of the led flash to communicate with the dfplayer, 
                and this disrupts the operation, then we can turn it off """
                Camera.flash_enabled[0] = state

        @staticmethod
        def retry(callback):
                """ Retry camera action and manage error """
                result = None
                if Camera.opened:
                        retry = 10
                        while 1:
                                if retry <= 0:
                                        tools.system.reboot("Reboot forced after camera problem")
                                try:
                                        result = callback()
                                        Camera.success[0] += 1
                                        break
                                except ValueError:
                                        Camera.failed[0] += 1
                                        Camera.new_failed[0] += 1
                                        if retry <= 3:
                                                tools.logger.syslog("Failed to get image %d retry before reset"%retry)
                                        retry -= 1
                                        time.sleep(0.5)
                        total = Camera.success[0] + Camera.failed[0]
                        STAT_CAMERA=20000
                        if (total % STAT_CAMERA) == 0:
                                if Camera.success[0] != 0:
                                        new_failed = 100.-((Camera.new_failed[0]*100)/STAT_CAMERA)
                                        failed    = 100.-((Camera.failed[0]*100)/total)
                                else:
                                        new_failed = 0.
                                        failed    = 0.
                                tools.logger.syslog("Camera stat : last %-3.1f%%, total %-3.1f%% success on %d"%(new_failed, failed, total))
                                Camera.new_failed[0] = 0
                return result

        @staticmethod
        async def reserve(object_, timeout=0, suspension=None):
                """ Reserve the camera, is used to stream the output of the camera
                to the web page. It stop the motion detection during this phase """
                return await Camera.reservation.reserve(object_, timeout, suspension)

        @staticmethod
        async def unreserve(object_):
                """ Unreserve the camera """
                return await Camera.reservation.unreserve(object_)

        @staticmethod
        def is_modified():
                """ Indicates that the camera configuration has been changed """
                return Camera.modified[0]

        @staticmethod
        def clear_modified():
                """ Reset the indicator of configuration modification """
                Camera.modified[0] = False

        @staticmethod
        def reinit():
                """ Close and open camera if get image previously asked, it avoid a DMA problem """
                if Camera.opened:
                        if Camera.aquisition[0] is True:
                                if "FREENOVE CAM" in os.uname().machine:
                                        Camera.close()
                                        Camera.open()

        @staticmethod
        def framesize(resolution=None):
                """ Configure the frame size """
                result = None
                val = None
                Camera.modified[0] = True
                if   resolution == b"UXGA"  or resolution == b"1600x1200" :val = camera.FRAMESIZE_UXGA
                elif resolution == b"SXGA"  or resolution == b"1280x1024" :val = camera.FRAMESIZE_SXGA
                elif resolution == b"XGA"   or resolution == b"1024x768"  :val = camera.FRAMESIZE_XGA
                elif resolution == b"SVGA"  or resolution == b"800x600"   :val = camera.FRAMESIZE_SVGA
                elif resolution == b"VGA"   or resolution == b"640x480"   :val = camera.FRAMESIZE_VGA
                elif resolution == b"CIF"   or resolution == b"400x296"   :val = camera.FRAMESIZE_CIF
                elif resolution == b"QVGA"  or resolution == b"320x240"   :val = camera.FRAMESIZE_QVGA
                elif resolution == b"HQVGA" or resolution == b"240x176"   :val = camera.FRAMESIZE_HQVGA
                elif resolution == b"QQVGA" or resolution == b"160x120"   :val = camera.FRAMESIZE_QQVGA
                if Camera.opened:
                        # print("Framesize %s"%strings.tostrings(resolution))
                        result = camera.framesize(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.framesize(val)
                return result

        @staticmethod
        def pixformat(format_=None):
                """ Change the format of image """
                result = None
                Camera.modified[0] = True
                val = None
                if   format_ == b"RGB565"    : val=camera.PIXFORMAT_RGB565
                elif format_ == b"YUV422"    : val=camera.PIXFORMAT_YUV422
                elif format_ == b"GRAYSCALE" : val=camera.PIXFORMAT_GRAYSCALE
                elif format_ == b"JPEG"      : val=camera.PIXFORMAT_JPEG
                elif format_ == b"RGB888"    : val=camera.PIXFORMAT_RGB888
                elif format_ == b"RAW"       : val=camera.PIXFORMAT_RAW
                elif format_ == b"RGB444"    : val=camera.PIXFORMAT_RGB444
                elif format_ == b"RGB555"    : val=camera.PIXFORMAT_RGB555
                if Camera.opened:
                        # print("Pixformat %s"%strings.tostrings(format_))
                        result = camera.pixformat(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.pixformat(val)
                return result

        @staticmethod
        def quality(val=None, modified=True):
                """ Configure the compression """
                result = None
                Camera.modified[0] = modified
                if Camera.opened:
                        # print("Quality %d"%val)
                        result = camera.quality(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.quality(val)
                return result

        @staticmethod
        def brightness(val=None):
                """ Change the brightness """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Brightness %d"%val)
                        result = camera.brightness(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.brightness(val)
                return result

        @staticmethod
        def contrast(val=None):
                """ Change the contrast """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Contrast %d"%val)
                        result = camera.contrast(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.contrast(val)
                return result

        @staticmethod
        def saturation(val=None):
                """ Change the saturation """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Saturation %d"%val)
                        result = camera.saturation(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.saturation(val)
                return result

        @staticmethod
        def sharpness(val=None):
                """ Change the sharpness """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Sharpness %d"%val)
                        result = camera.sharpness(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.sharpness(val)
                return result

        @staticmethod
        def hmirror(val=None):
                """ Set horizontal mirroring """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Hmirror %d"%val)
                        result = camera.hmirror(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.hmirror(val)
                return result

        @staticmethod
        def vflip(val=None):
                """ Set the vertical flip """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Vflip %d"%val)
                        result = camera.vflip(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.vflip(val)
                return result

        @staticmethod
        def configure(config):
                """ Configure the camera """
                if Camera.opened:
                        Camera.pixformat (config.pixformat)
                        Camera.framesize (config.framesize)
                        Camera.quality   (config.quality)
                        Camera.brightness(config.brightness)
                        Camera.contrast  (config.contrast)
                        Camera.saturation(config.saturation)
                        Camera.hmirror   (config.hmirror)
                        Camera.vflip     (config.vflip)
                        Camera.flash     (config.flash_level)

        @staticmethod
        def get_config():
                """ Reload configuration if it changed """
                if Camera.config is None:
                        Camera.config = CameraConfig()
                        if Camera.config.load() is False:
                                Camera.config.save()
                else:
                        Camera.config.refresh()
                return Camera.config

        @staticmethod
        def is_activated():
                """ Indicates if the camera is configured to be activated """
                if Camera.config is None:
                        Camera.get_config()
                if Camera.config is not None:
                        return Camera.config.activated
                else:
                        return False

Classes

class Camera

Singleton class to manage the camera

Expand source code
class Camera:
        """ Singleton class to manage the camera """
        reservation = Reservation()
        opened = False
        lock = uasyncio.Lock()
        modified = [False]
        success = [0]
        failed  = [0]
        new_failed = [0]
        config = None
        flash_enabled = [True]
        aquisition = [False]

        @staticmethod
        def gpio_config(**kwargs):
                """ Configure the structure for camera initialization. It must be done before the first call of Camera.open.
                        The defaults values are for ESP32CAM.
                        - pin_pwdn           : GPIO pin for camera power down line
                        - pin_reset          : GPIO pin for camera pin_reset line
                        - pin_xclk           : GPIO pin for camera XCLK line
                        - pin_sscb_sda       : GPIO pin for camera SDA line (SIOD)
                        - pin_sscb_scl       : GPIO pin for camera SCL line (SIOC)
                        - pin_d7             : GPIO pin for camera D7 or Y9 line
                        - pin_d6             : GPIO pin for camera D6 or Y8 line
                        - pin_d5             : GPIO pin for camera D5 or Y7 line
                        - pin_d4             : GPIO pin for camera D4 or Y6 line
                        - pin_d3             : GPIO pin for camera D3 or Y5 line
                        - pin_d2             : GPIO pin for camera D2 or Y4 line
                        - pin_d1             : GPIO pin for camera D1 or Y3 line
                        - pin_d0             : GPIO pin for camera D0 or Y2 line
                        - pin_vsync          : GPIO pin for camera VSYNC line
                        - pin_href           : GPIO pin for camera HREF line
                        - pin_pclk           : GPIO pin for camera PCLK line
                        - xclk_freq_hz       : Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental)
                        - ledc_timer         : LEDC timer to be used for generating XCLK
                        - ledc_channel       : LEDC channel to be used for generating XCLK
                        - pixel_format       : Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG
                        - frame_size         : Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
                        - jpeg_quality       : Quality of JPEG output. 0-63 lower means higher quality
                        - fb_count           : Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)
                        - flash_led          : GPIO pin for flash led or 0 to disable 
                        - grab_mode          : 0=CAMERA_GRAB_WHEN_EMPTY, 1=CAMERA_GRAB_LATEST
                        - fb_location        : 0=CAMERA_FB_IN_PSRAM, 1=CAMERA_FB_IN_DRAM
"""
                Camera.get_config()
                if Camera.is_activated():
                        camera.configure(**kwargs)

        @staticmethod
        def open():
                """ Open the camera """
                Camera.get_config()
                if Camera.is_activated():
                        result = True
                        if Camera.opened is False:
                                for i in range(10):
                                        res = camera.init()
                                        if res is False:
                                                # print("Camera not initialized")
                                                camera.deinit()
                                                time.sleep(0.5)
                                        else:
                                                break
                                else:
                                        result = False

                                if result:
                                        # Photo on 800x600, motion detection / 8 (100x75), each square detection 8x8 (12.5 x 9.375)
                                        Camera.opened = True
                                        Camera.aquisition[0] = False
                else:
                        result = False
                return result

        @staticmethod
        def get_stat():
                """ Statistic """
                return Camera.success[0], Camera.failed[0]

        @staticmethod
        def reset_stat():
                """ Reset statistic """
                Camera.success[0] = 0
                Camera.failed [0] = 0
                Camera.new_failed[0] = 0

        @staticmethod
        def close():
                """ Close the camera """
                if Camera.opened is True:
                        camera.deinit()
                        Camera.opened = False

        @staticmethod
        def reset():
                """ Reset the camera """
                if Camera.opened is True:
                        camera.reset()

        @staticmethod
        def is_opened():
                """ Indicates if the camera opened """
                return Camera.opened

        @staticmethod
        def capture():
                """ Capture an image on the camera """
                Camera.aquisition[0] = True
                return Camera.retry(camera.capture)

        @staticmethod
        def motion():
                """ Get the motion informations.
                This contains a jpeg image, with matrices of the different color RGB """
                Camera.aquisition[0] = True
                return Camera.retry(camera.motion)

        @staticmethod
        def flash(level=0):
                """ Start or stop the flash """
                if Camera.flash_enabled[0]:
                        camera.flash(level)

        @staticmethod
        def set_flash(state):
                """ On the esp32cam, we use the output of the led flash to communicate with the dfplayer, 
                and this disrupts the operation, then we can turn it off """
                Camera.flash_enabled[0] = state

        @staticmethod
        def retry(callback):
                """ Retry camera action and manage error """
                result = None
                if Camera.opened:
                        retry = 10
                        while 1:
                                if retry <= 0:
                                        tools.system.reboot("Reboot forced after camera problem")
                                try:
                                        result = callback()
                                        Camera.success[0] += 1
                                        break
                                except ValueError:
                                        Camera.failed[0] += 1
                                        Camera.new_failed[0] += 1
                                        if retry <= 3:
                                                tools.logger.syslog("Failed to get image %d retry before reset"%retry)
                                        retry -= 1
                                        time.sleep(0.5)
                        total = Camera.success[0] + Camera.failed[0]
                        STAT_CAMERA=20000
                        if (total % STAT_CAMERA) == 0:
                                if Camera.success[0] != 0:
                                        new_failed = 100.-((Camera.new_failed[0]*100)/STAT_CAMERA)
                                        failed    = 100.-((Camera.failed[0]*100)/total)
                                else:
                                        new_failed = 0.
                                        failed    = 0.
                                tools.logger.syslog("Camera stat : last %-3.1f%%, total %-3.1f%% success on %d"%(new_failed, failed, total))
                                Camera.new_failed[0] = 0
                return result

        @staticmethod
        async def reserve(object_, timeout=0, suspension=None):
                """ Reserve the camera, is used to stream the output of the camera
                to the web page. It stop the motion detection during this phase """
                return await Camera.reservation.reserve(object_, timeout, suspension)

        @staticmethod
        async def unreserve(object_):
                """ Unreserve the camera """
                return await Camera.reservation.unreserve(object_)

        @staticmethod
        def is_modified():
                """ Indicates that the camera configuration has been changed """
                return Camera.modified[0]

        @staticmethod
        def clear_modified():
                """ Reset the indicator of configuration modification """
                Camera.modified[0] = False

        @staticmethod
        def reinit():
                """ Close and open camera if get image previously asked, it avoid a DMA problem """
                if Camera.opened:
                        if Camera.aquisition[0] is True:
                                if "FREENOVE CAM" in os.uname().machine:
                                        Camera.close()
                                        Camera.open()

        @staticmethod
        def framesize(resolution=None):
                """ Configure the frame size """
                result = None
                val = None
                Camera.modified[0] = True
                if   resolution == b"UXGA"  or resolution == b"1600x1200" :val = camera.FRAMESIZE_UXGA
                elif resolution == b"SXGA"  or resolution == b"1280x1024" :val = camera.FRAMESIZE_SXGA
                elif resolution == b"XGA"   or resolution == b"1024x768"  :val = camera.FRAMESIZE_XGA
                elif resolution == b"SVGA"  or resolution == b"800x600"   :val = camera.FRAMESIZE_SVGA
                elif resolution == b"VGA"   or resolution == b"640x480"   :val = camera.FRAMESIZE_VGA
                elif resolution == b"CIF"   or resolution == b"400x296"   :val = camera.FRAMESIZE_CIF
                elif resolution == b"QVGA"  or resolution == b"320x240"   :val = camera.FRAMESIZE_QVGA
                elif resolution == b"HQVGA" or resolution == b"240x176"   :val = camera.FRAMESIZE_HQVGA
                elif resolution == b"QQVGA" or resolution == b"160x120"   :val = camera.FRAMESIZE_QQVGA
                if Camera.opened:
                        # print("Framesize %s"%strings.tostrings(resolution))
                        result = camera.framesize(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.framesize(val)
                return result

        @staticmethod
        def pixformat(format_=None):
                """ Change the format of image """
                result = None
                Camera.modified[0] = True
                val = None
                if   format_ == b"RGB565"    : val=camera.PIXFORMAT_RGB565
                elif format_ == b"YUV422"    : val=camera.PIXFORMAT_YUV422
                elif format_ == b"GRAYSCALE" : val=camera.PIXFORMAT_GRAYSCALE
                elif format_ == b"JPEG"      : val=camera.PIXFORMAT_JPEG
                elif format_ == b"RGB888"    : val=camera.PIXFORMAT_RGB888
                elif format_ == b"RAW"       : val=camera.PIXFORMAT_RAW
                elif format_ == b"RGB444"    : val=camera.PIXFORMAT_RGB444
                elif format_ == b"RGB555"    : val=camera.PIXFORMAT_RGB555
                if Camera.opened:
                        # print("Pixformat %s"%strings.tostrings(format_))
                        result = camera.pixformat(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.pixformat(val)
                return result

        @staticmethod
        def quality(val=None, modified=True):
                """ Configure the compression """
                result = None
                Camera.modified[0] = modified
                if Camera.opened:
                        # print("Quality %d"%val)
                        result = camera.quality(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.quality(val)
                return result

        @staticmethod
        def brightness(val=None):
                """ Change the brightness """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Brightness %d"%val)
                        result = camera.brightness(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.brightness(val)
                return result

        @staticmethod
        def contrast(val=None):
                """ Change the contrast """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Contrast %d"%val)
                        result = camera.contrast(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.contrast(val)
                return result

        @staticmethod
        def saturation(val=None):
                """ Change the saturation """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Saturation %d"%val)
                        result = camera.saturation(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.saturation(val)
                return result

        @staticmethod
        def sharpness(val=None):
                """ Change the sharpness """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Sharpness %d"%val)
                        result = camera.sharpness(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.sharpness(val)
                return result

        @staticmethod
        def hmirror(val=None):
                """ Set horizontal mirroring """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Hmirror %d"%val)
                        result = camera.hmirror(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.hmirror(val)
                return result

        @staticmethod
        def vflip(val=None):
                """ Set the vertical flip """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Vflip %d"%val)
                        result = camera.vflip(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.vflip(val)
                return result

        @staticmethod
        def configure(config):
                """ Configure the camera """
                if Camera.opened:
                        Camera.pixformat (config.pixformat)
                        Camera.framesize (config.framesize)
                        Camera.quality   (config.quality)
                        Camera.brightness(config.brightness)
                        Camera.contrast  (config.contrast)
                        Camera.saturation(config.saturation)
                        Camera.hmirror   (config.hmirror)
                        Camera.vflip     (config.vflip)
                        Camera.flash     (config.flash_level)

        @staticmethod
        def get_config():
                """ Reload configuration if it changed """
                if Camera.config is None:
                        Camera.config = CameraConfig()
                        if Camera.config.load() is False:
                                Camera.config.save()
                else:
                        Camera.config.refresh()
                return Camera.config

        @staticmethod
        def is_activated():
                """ Indicates if the camera is configured to be activated """
                if Camera.config is None:
                        Camera.get_config()
                if Camera.config is not None:
                        return Camera.config.activated
                else:
                        return False

Class variables

var aquisition
var config
var failed
var flash_enabled
var lock
var modified
var new_failed
var opened
var reservation
var success

Static methods

def brightness(val=None)

Change the brightness

Expand source code
@staticmethod
def brightness(val=None):
        """ Change the brightness """
        result = None
        Camera.modified[0] = True
        if Camera.opened:
                # print("Brightness %d"%val)
                result = camera.brightness(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.brightness(val)
        return result
def capture()

Capture an image on the camera

Expand source code
@staticmethod
def capture():
        """ Capture an image on the camera """
        Camera.aquisition[0] = True
        return Camera.retry(camera.capture)
def clear_modified()

Reset the indicator of configuration modification

Expand source code
@staticmethod
def clear_modified():
        """ Reset the indicator of configuration modification """
        Camera.modified[0] = False
def close()

Close the camera

Expand source code
@staticmethod
def close():
        """ Close the camera """
        if Camera.opened is True:
                camera.deinit()
                Camera.opened = False
def configure(config)

Configure the camera

Expand source code
@staticmethod
def configure(config):
        """ Configure the camera """
        if Camera.opened:
                Camera.pixformat (config.pixformat)
                Camera.framesize (config.framesize)
                Camera.quality   (config.quality)
                Camera.brightness(config.brightness)
                Camera.contrast  (config.contrast)
                Camera.saturation(config.saturation)
                Camera.hmirror   (config.hmirror)
                Camera.vflip     (config.vflip)
                Camera.flash     (config.flash_level)
def contrast(val=None)

Change the contrast

Expand source code
@staticmethod
def contrast(val=None):
        """ Change the contrast """
        result = None
        Camera.modified[0] = True
        if Camera.opened:
                # print("Contrast %d"%val)
                result = camera.contrast(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.contrast(val)
        return result
def flash(level=0)

Start or stop the flash

Expand source code
@staticmethod
def flash(level=0):
        """ Start or stop the flash """
        if Camera.flash_enabled[0]:
                camera.flash(level)
def framesize(resolution=None)

Configure the frame size

Expand source code
@staticmethod
def framesize(resolution=None):
        """ Configure the frame size """
        result = None
        val = None
        Camera.modified[0] = True
        if   resolution == b"UXGA"  or resolution == b"1600x1200" :val = camera.FRAMESIZE_UXGA
        elif resolution == b"SXGA"  or resolution == b"1280x1024" :val = camera.FRAMESIZE_SXGA
        elif resolution == b"XGA"   or resolution == b"1024x768"  :val = camera.FRAMESIZE_XGA
        elif resolution == b"SVGA"  or resolution == b"800x600"   :val = camera.FRAMESIZE_SVGA
        elif resolution == b"VGA"   or resolution == b"640x480"   :val = camera.FRAMESIZE_VGA
        elif resolution == b"CIF"   or resolution == b"400x296"   :val = camera.FRAMESIZE_CIF
        elif resolution == b"QVGA"  or resolution == b"320x240"   :val = camera.FRAMESIZE_QVGA
        elif resolution == b"HQVGA" or resolution == b"240x176"   :val = camera.FRAMESIZE_HQVGA
        elif resolution == b"QQVGA" or resolution == b"160x120"   :val = camera.FRAMESIZE_QQVGA
        if Camera.opened:
                # print("Framesize %s"%strings.tostrings(resolution))
                result = camera.framesize(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.framesize(val)
        return result
def get_config()

Reload configuration if it changed

Expand source code
@staticmethod
def get_config():
        """ Reload configuration if it changed """
        if Camera.config is None:
                Camera.config = CameraConfig()
                if Camera.config.load() is False:
                        Camera.config.save()
        else:
                Camera.config.refresh()
        return Camera.config
def get_stat()

Statistic

Expand source code
@staticmethod
def get_stat():
        """ Statistic """
        return Camera.success[0], Camera.failed[0]
def gpio_config(**kwargs)

Configure the structure for camera initialization. It must be done before the first call of Camera.open. The defaults values are for ESP32CAM. - pin_pwdn : GPIO pin for camera power down line - pin_reset : GPIO pin for camera pin_reset line - pin_xclk : GPIO pin for camera XCLK line - pin_sscb_sda : GPIO pin for camera SDA line (SIOD) - pin_sscb_scl : GPIO pin for camera SCL line (SIOC) - pin_d7 : GPIO pin for camera D7 or Y9 line - pin_d6 : GPIO pin for camera D6 or Y8 line - pin_d5 : GPIO pin for camera D5 or Y7 line - pin_d4 : GPIO pin for camera D4 or Y6 line - pin_d3 : GPIO pin for camera D3 or Y5 line - pin_d2 : GPIO pin for camera D2 or Y4 line - pin_d1 : GPIO pin for camera D1 or Y3 line - pin_d0 : GPIO pin for camera D0 or Y2 line - pin_vsync : GPIO pin for camera VSYNC line - pin_href : GPIO pin for camera HREF line - pin_pclk : GPIO pin for camera PCLK line - xclk_freq_hz : Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental) - ledc_timer : LEDC timer to be used for generating XCLK - ledc_channel : LEDC channel to be used for generating XCLK - pixel_format : Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG - frame_size : Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA - jpeg_quality : Quality of JPEG output. 0-63 lower means higher quality - fb_count : Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) - flash_led : GPIO pin for flash led or 0 to disable - grab_mode : 0=CAMERA_GRAB_WHEN_EMPTY, 1=CAMERA_GRAB_LATEST - fb_location : 0=CAMERA_FB_IN_PSRAM, 1=CAMERA_FB_IN_DRAM

Expand source code
        @staticmethod
        def gpio_config(**kwargs):
                """ Configure the structure for camera initialization. It must be done before the first call of Camera.open.
                        The defaults values are for ESP32CAM.
                        - pin_pwdn           : GPIO pin for camera power down line
                        - pin_reset          : GPIO pin for camera pin_reset line
                        - pin_xclk           : GPIO pin for camera XCLK line
                        - pin_sscb_sda       : GPIO pin for camera SDA line (SIOD)
                        - pin_sscb_scl       : GPIO pin for camera SCL line (SIOC)
                        - pin_d7             : GPIO pin for camera D7 or Y9 line
                        - pin_d6             : GPIO pin for camera D6 or Y8 line
                        - pin_d5             : GPIO pin for camera D5 or Y7 line
                        - pin_d4             : GPIO pin for camera D4 or Y6 line
                        - pin_d3             : GPIO pin for camera D3 or Y5 line
                        - pin_d2             : GPIO pin for camera D2 or Y4 line
                        - pin_d1             : GPIO pin for camera D1 or Y3 line
                        - pin_d0             : GPIO pin for camera D0 or Y2 line
                        - pin_vsync          : GPIO pin for camera VSYNC line
                        - pin_href           : GPIO pin for camera HREF line
                        - pin_pclk           : GPIO pin for camera PCLK line
                        - xclk_freq_hz       : Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental)
                        - ledc_timer         : LEDC timer to be used for generating XCLK
                        - ledc_channel       : LEDC channel to be used for generating XCLK
                        - pixel_format       : Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG
                        - frame_size         : Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
                        - jpeg_quality       : Quality of JPEG output. 0-63 lower means higher quality
                        - fb_count           : Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)
                        - flash_led          : GPIO pin for flash led or 0 to disable 
                        - grab_mode          : 0=CAMERA_GRAB_WHEN_EMPTY, 1=CAMERA_GRAB_LATEST
                        - fb_location        : 0=CAMERA_FB_IN_PSRAM, 1=CAMERA_FB_IN_DRAM
"""
                Camera.get_config()
                if Camera.is_activated():
                        camera.configure(**kwargs)
def hmirror(val=None)

Set horizontal mirroring

Expand source code
@staticmethod
def hmirror(val=None):
        """ Set horizontal mirroring """
        result = None
        Camera.modified[0] = True
        if Camera.opened:
                # print("Hmirror %d"%val)
                result = camera.hmirror(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.hmirror(val)
        return result
def is_activated()

Indicates if the camera is configured to be activated

Expand source code
@staticmethod
def is_activated():
        """ Indicates if the camera is configured to be activated """
        if Camera.config is None:
                Camera.get_config()
        if Camera.config is not None:
                return Camera.config.activated
        else:
                return False
def is_modified()

Indicates that the camera configuration has been changed

Expand source code
@staticmethod
def is_modified():
        """ Indicates that the camera configuration has been changed """
        return Camera.modified[0]
def is_opened()

Indicates if the camera opened

Expand source code
@staticmethod
def is_opened():
        """ Indicates if the camera opened """
        return Camera.opened
def motion()

Get the motion informations. This contains a jpeg image, with matrices of the different color RGB

Expand source code
@staticmethod
def motion():
        """ Get the motion informations.
        This contains a jpeg image, with matrices of the different color RGB """
        Camera.aquisition[0] = True
        return Camera.retry(camera.motion)
def open()

Open the camera

Expand source code
@staticmethod
def open():
        """ Open the camera """
        Camera.get_config()
        if Camera.is_activated():
                result = True
                if Camera.opened is False:
                        for i in range(10):
                                res = camera.init()
                                if res is False:
                                        # print("Camera not initialized")
                                        camera.deinit()
                                        time.sleep(0.5)
                                else:
                                        break
                        else:
                                result = False

                        if result:
                                # Photo on 800x600, motion detection / 8 (100x75), each square detection 8x8 (12.5 x 9.375)
                                Camera.opened = True
                                Camera.aquisition[0] = False
        else:
                result = False
        return result
def pixformat(format_=None)

Change the format of image

Expand source code
@staticmethod
def pixformat(format_=None):
        """ Change the format of image """
        result = None
        Camera.modified[0] = True
        val = None
        if   format_ == b"RGB565"    : val=camera.PIXFORMAT_RGB565
        elif format_ == b"YUV422"    : val=camera.PIXFORMAT_YUV422
        elif format_ == b"GRAYSCALE" : val=camera.PIXFORMAT_GRAYSCALE
        elif format_ == b"JPEG"      : val=camera.PIXFORMAT_JPEG
        elif format_ == b"RGB888"    : val=camera.PIXFORMAT_RGB888
        elif format_ == b"RAW"       : val=camera.PIXFORMAT_RAW
        elif format_ == b"RGB444"    : val=camera.PIXFORMAT_RGB444
        elif format_ == b"RGB555"    : val=camera.PIXFORMAT_RGB555
        if Camera.opened:
                # print("Pixformat %s"%strings.tostrings(format_))
                result = camera.pixformat(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.pixformat(val)
        return result
def quality(val=None, modified=True)

Configure the compression

Expand source code
@staticmethod
def quality(val=None, modified=True):
        """ Configure the compression """
        result = None
        Camera.modified[0] = modified
        if Camera.opened:
                # print("Quality %d"%val)
                result = camera.quality(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.quality(val)
        return result
def reinit()

Close and open camera if get image previously asked, it avoid a DMA problem

Expand source code
@staticmethod
def reinit():
        """ Close and open camera if get image previously asked, it avoid a DMA problem """
        if Camera.opened:
                if Camera.aquisition[0] is True:
                        if "FREENOVE CAM" in os.uname().machine:
                                Camera.close()
                                Camera.open()
async def reserve(object_, timeout=0, suspension=None)

Reserve the camera, is used to stream the output of the camera to the web page. It stop the motion detection during this phase

Expand source code
@staticmethod
async def reserve(object_, timeout=0, suspension=None):
        """ Reserve the camera, is used to stream the output of the camera
        to the web page. It stop the motion detection during this phase """
        return await Camera.reservation.reserve(object_, timeout, suspension)
def reset()

Reset the camera

Expand source code
@staticmethod
def reset():
        """ Reset the camera """
        if Camera.opened is True:
                camera.reset()
def reset_stat()

Reset statistic

Expand source code
@staticmethod
def reset_stat():
        """ Reset statistic """
        Camera.success[0] = 0
        Camera.failed [0] = 0
        Camera.new_failed[0] = 0
def retry(callback)

Retry camera action and manage error

Expand source code
@staticmethod
def retry(callback):
        """ Retry camera action and manage error """
        result = None
        if Camera.opened:
                retry = 10
                while 1:
                        if retry <= 0:
                                tools.system.reboot("Reboot forced after camera problem")
                        try:
                                result = callback()
                                Camera.success[0] += 1
                                break
                        except ValueError:
                                Camera.failed[0] += 1
                                Camera.new_failed[0] += 1
                                if retry <= 3:
                                        tools.logger.syslog("Failed to get image %d retry before reset"%retry)
                                retry -= 1
                                time.sleep(0.5)
                total = Camera.success[0] + Camera.failed[0]
                STAT_CAMERA=20000
                if (total % STAT_CAMERA) == 0:
                        if Camera.success[0] != 0:
                                new_failed = 100.-((Camera.new_failed[0]*100)/STAT_CAMERA)
                                failed    = 100.-((Camera.failed[0]*100)/total)
                        else:
                                new_failed = 0.
                                failed    = 0.
                        tools.logger.syslog("Camera stat : last %-3.1f%%, total %-3.1f%% success on %d"%(new_failed, failed, total))
                        Camera.new_failed[0] = 0
        return result
def saturation(val=None)

Change the saturation

Expand source code
@staticmethod
def saturation(val=None):
        """ Change the saturation """
        result = None
        Camera.modified[0] = True
        if Camera.opened:
                # print("Saturation %d"%val)
                result = camera.saturation(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.saturation(val)
        return result
def set_flash(state)

On the esp32cam, we use the output of the led flash to communicate with the dfplayer, and this disrupts the operation, then we can turn it off

Expand source code
@staticmethod
def set_flash(state):
        """ On the esp32cam, we use the output of the led flash to communicate with the dfplayer, 
        and this disrupts the operation, then we can turn it off """
        Camera.flash_enabled[0] = state
def sharpness(val=None)

Change the sharpness

Expand source code
@staticmethod
def sharpness(val=None):
        """ Change the sharpness """
        result = None
        Camera.modified[0] = True
        if Camera.opened:
                # print("Sharpness %d"%val)
                result = camera.sharpness(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.sharpness(val)
        return result
async def unreserve(object_)

Unreserve the camera

Expand source code
@staticmethod
async def unreserve(object_):
        """ Unreserve the camera """
        return await Camera.reservation.unreserve(object_)
def vflip(val=None)

Set the vertical flip

Expand source code
@staticmethod
def vflip(val=None):
        """ Set the vertical flip """
        result = None
        Camera.modified[0] = True
        if Camera.opened:
                # print("Vflip %d"%val)
                result = camera.vflip(val)
                if result is False and val is not None:
                        Camera.reinit()
                        result = camera.vflip(val)
        return result
class CameraConfig

Class that collects the camera rendering configuration

Constructor

Expand source code
class CameraConfig(tools.jsonconfig.JsonConfig):
        """ Class that collects the camera rendering configuration """
        def __init__(self):
                """ Constructor """
                tools.jsonconfig.JsonConfig.__init__(self)
                self.activated  = True
                self.framesize  = b"640x480"
                self.pixformat  = b"JPEG"
                self.quality    = 25
                self.brightness = 0
                self.contrast   = 0
                self.saturation = 0
                self.hmirror    = False
                self.vflip      = False
                self.flash_level = 0

Ancestors

  • tools.jsonconfig.JsonConfig
class Reservation

Manage the camera reservation

Constructor

Expand source code
class Reservation:
        """ Manage the camera reservation """
        def __init__(self):
                """ Constructor """
                self.identifier = None
                self.count = 0
                self.lock = uasyncio.Lock()
                self.suspended = 0

        async def reserve(self, object_, timeout=0, suspension=None):
                """ Wait the ressource and reserve """
                result = False
                # Wait
                while True:
                        result = await self.acquire(object_, suspension)
                        if result:
                                break
                        timeout -= 1
                        if timeout < 0:
                                break
                        await uasyncio.sleep_ms(1000)
                return result

        async def acquire(self, object_, suspension=None):
                """ Reserve the camera, is used to stream the output of the camera
                to the web page. It stop the motion detection during this phase """
                result = False
                await self.lock.acquire()
                identifier = id(object_)
                try:
                        # If not reserved
                        if self.identifier is None:
                                # If suspension not required
                                if suspension is None:
                                        # If previous suspension ended
                                        if self.suspended <= 0:
                                                # Reserve
                                                self.identifier = identifier
                                                self.count = 1
                                                self.suspended = 0
                                                result = True
                                        else:
                                                # Decrease suspension counter
                                                self.suspended -= 1
                                else:
                                        # Reserve
                                        self.identifier = identifier
                                        self.count = 1
                                        self.suspended = suspension
                                        result = True
                        # If already reserved by the current object_
                        elif self.identifier == identifier:
                                # Increase reservation counter
                                self.count += 1
                                self.suspended = suspension
                                result = True
                finally:
                        self.lock.release()
                return result

        async def unreserve(self, object_):
                """ Unreserve the camera """
                result = False
                await self.lock.acquire()
                identifier = id(object_)
                try:
                        if self.identifier == identifier:
                                if self.count <= 1:
                                        self.count = 0
                                        self.identifier = None
                                else:
                                        self.count -= 1
                                result = True
                finally:
                        self.lock.release()
                return result

Methods

async def acquire(self, object_, suspension=None)

Reserve the camera, is used to stream the output of the camera to the web page. It stop the motion detection during this phase

Expand source code
async def acquire(self, object_, suspension=None):
        """ Reserve the camera, is used to stream the output of the camera
        to the web page. It stop the motion detection during this phase """
        result = False
        await self.lock.acquire()
        identifier = id(object_)
        try:
                # If not reserved
                if self.identifier is None:
                        # If suspension not required
                        if suspension is None:
                                # If previous suspension ended
                                if self.suspended <= 0:
                                        # Reserve
                                        self.identifier = identifier
                                        self.count = 1
                                        self.suspended = 0
                                        result = True
                                else:
                                        # Decrease suspension counter
                                        self.suspended -= 1
                        else:
                                # Reserve
                                self.identifier = identifier
                                self.count = 1
                                self.suspended = suspension
                                result = True
                # If already reserved by the current object_
                elif self.identifier == identifier:
                        # Increase reservation counter
                        self.count += 1
                        self.suspended = suspension
                        result = True
        finally:
                self.lock.release()
        return result
async def reserve(self, object_, timeout=0, suspension=None)

Wait the ressource and reserve

Expand source code
async def reserve(self, object_, timeout=0, suspension=None):
        """ Wait the ressource and reserve """
        result = False
        # Wait
        while True:
                result = await self.acquire(object_, suspension)
                if result:
                        break
                timeout -= 1
                if timeout < 0:
                        break
                await uasyncio.sleep_ms(1000)
        return result
async def unreserve(self, object_)

Unreserve the camera

Expand source code
async def unreserve(self, object_):
        """ Unreserve the camera """
        result = False
        await self.lock.acquire()
        identifier = id(object_)
        try:
                if self.identifier == identifier:
                        if self.count <= 1:
                                self.count = 0
                                self.identifier = None
                        else:
                                self.count -= 1
                        result = True
        finally:
                self.lock.release()
        return result