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

Camera eposure time doubled from shutter speed? #726

Open
minhlead opened this issue Oct 18, 2022 · 0 comments
Open

Camera eposure time doubled from shutter speed? #726

minhlead opened this issue Oct 18, 2022 · 0 comments

Comments

@minhlead
Copy link

Some how my pi HQ takes 2x the shutter speed time to complete an exposure. I.e: if the shutter speed is 1 minutes, the exposure will take exactly 2 minutes to complete. I can see the camera writes a 0 byte file when the exposure start and fill it up with data after the 2x time passed.
I tried to enable still_stats, disable noise_reduction, turn off awb_mode, exposure_mode, turn on burst and nothing worked.
Am I doing something wrong? Here is the code
`#!/usr/bin/python3

#-----------------------------------------------------------------------

Time lapse service for shooting continuously single images with

automatic exposure adaption day and night.

Copyright (C) 2021 Wolfgang Reissenberger sterne-jaeger@openfuture.de

This application is free software; you can redistribute it and/or

modify it under the terms of the GNU General Public

License as published by the Free Software Foundation; either

version 2 of the License, or (at your option) any later version.

#-----------------------------------------------------------------------

import io, os, stat, sys, math
from time import time, sleep, perf_counter
from datetime import datetime
from configparser import ConfigParser
from pathlib import Path
from picamera import PiCamera
from picamera import Color
from fractions import Fraction
from autoexposure import *

class TimelapseService:
config = None
inifile_name = None
stopfile = None
oldname = None
rates = []
last_shot = 0

def __init__(self):
    # setup configuration
    self.config = ConfigParser(interpolation=None)
    self.config.optionxform = str
    # default values
    self.config.add_section('Camera')
    self.config.set('Camera', 'SensorMode', '3')
    self.config.set('Camera', 'ExposureTime', '400000') # 1/250 sec
    self.config.set('Camera', 'resX', '3040')
    self.config.set('Camera', 'resY', '3040')
    self.config.set('Camera', 'BaseDirectory', "/usr/share/weatherradio/html/media/snapshots")
    self.config.set('Camera', 'ConverterFIFO', "/tmp/imageconverter.fifo")
    self.config.set('Camera', 'ISOSpeedRatings', '50')
    self.config.set('Camera', 'Contrast', '0')
    self.config.set('Camera', 'Brightness', '50')
    self.config.set('Camera', 'Saturation', '0')
    self.config.set('Camera', 'interval', '60')
    self.config.set('Camera', 'zoomX0', '0.0')
    self.config.set('Camera', 'zoomY0', '0.0')
    self.config.set('Camera', 'zoomX1', '1.0')
    self.config.set('Camera', 'zoomY1', '1.0')
    self.config.set('Camera', 'ExposureTimeout', '450')

    # night default settings
    self.config.add_section('Night')
    self.config.set('Night', 'Contrast', '100')
    self.config.set('Night', 'Brightness', '78')
    self.config.set('Night', 'Saturation', '0')
    self.config.set('Night', 'MaxExposure', '50000000') # 20 sec
    self.config.set('Night', 'MaxISO', '300')

    # read ini file
    self.inifile_name = os.path.dirname(os.path.realpath(__file__)) + '/timelapse.ini'
    self.config.read(self.inifile_name)

    self.stopfile = Path('/tmp/timelapse.stop')


def config_camera(self, camera):
    # change to auto exposure for short exposure times
    if self.config.getint('Camera', 'ExposureTime') < 20:
        camera.shutter_speed = 0 # auto
    else:
        camera.shutter_speed = self.config.getint('Camera', 'ExposureTime')

    camera.iso           = self.config.getint('Camera', 'ISOSpeedRatings')
    camera.contrast      = self.config.getint('Camera', 'Contrast')
    camera.brightness    = self.config.getint('Camera', 'Brightness')
    camera.saturation    = self.config.getint('Camera', 'Saturation')
    camera.zoom          = (self.config.getfloat('Camera', 'zoomX0'),
                            self.config.getfloat('Camera', 'zoomY0'),
                            self.config.getfloat('Camera', 'zoomX1'),
                            self.config.getfloat('Camera', 'zoomY1'))

def get_capture_dir(self):
    dir = self.config.get('Camera', 'BaseDirectory')
    # ensure that the image directory exists
    if not Path(dir).exists():
        Path(dir).mkdir(parents=True)
    return dir

def get_target_dir(self, now):
    targetdir = self.config.get('Camera', 'BaseDirectory') + '/' + now.strftime("%Y-%m-%d")
    # ensure that the image directory exists
    if not Path(targetdir).exists():
        Path(targetdir).mkdir(parents=True)
    return targetdir

def get_image_name(self, now, dir=None):
    filename = now.strftime("%Y-%m-%d_%H%M%S") + ".jpg"
    if dir != None:
        filename = dir + '/' + filename
    return filename


def convert_image(self, now, fullname):
    if not self.stopfile.is_file():
        fifo = self.config.get('Camera', 'ConverterFIFO')
        if os.path.exists(fifo) and stat.S_ISFIFO(os.stat(fifo).st_mode):
            with open(fifo, 'w') as pipeout:
                pipeout.write("%s\n" % self.get_image_name(now))
        else:
            # otherwise move image to target directory
            target = self.get_image_name(now, dir=self.get_target_dir(now))
            # mv to target directory
            os.rename(fullname, target)
        # remember the current name
        self.oldname = fullname


def wait_for_converter(self, fullname):
    # wait only if there exists a pipe to the converter
    fifo = self.config.get('Camera', 'ConverterFIFO')
    if os.path.exists(fifo) and stat.S_ISFIFO(os.stat(fifo).st_mode):
        # wait until old image has disappeared
        while self.oldname is not None and Path(self.oldname).is_file() and not self.stopfile.is_file():
            print("%s waiting for image converter ..." % fullname)
            sleep(1)

def single_shot(self, interval, camera):
    # set base parameters that cannot be changed
    self.config_camera(camera)
    # calculate wait time in seconds
    ts_now = time()
    # ensure that diff > interval and or on a 5 secs edge
    diff = ts_now - self.last_shot
    if diff > interval:
        # next 5 secs interval
        diff = (int(ts_now / 5)+1)*5-ts_now
    else:
        # fill up until interval
        diff = interval - diff
    sleep(diff)
    # current capture time
    ts_now = time()
    # start capturing
    start_capture = perf_counter()
    print("Start capturing...")
    now = datetime.fromtimestamp(ts_now)
    fullname = self.get_image_name(now, dir=self.get_capture_dir())
    camera.awb_mode = 'off'
    camera.awb_gains = (1.4, 1.7)
    camera.exposure_mode = 'off'
    fullname = self.get_image_name(now, dir=self.get_capture_dir())
    camera.annotate_foreground = Color('white')
    camera.annotate_background = Color('black')
    camera.annotate_text = "%s | ex=%.3f ms | iso=%d " % (now.strftime("%Y-%m-%d %H:%M:%S"), camera.shutter_speed/1000, camera.iso)
    camera.annotate_text_size = 72
    camera.still_stats = True
    camera.image_denoise = False
    camera.capture_sequence([fullname],burst=True)
    self.last_shot = ts_now

    # wait for the conversion of the previous image to be completed
    self.wait_for_converter(fullname)
    # calculate the optimal exposure time
    (imgExpTime, imgBrightness) = calibrateExpTime(fullname, self.config)

    # store new configuration
    configfile = open(self.inifile_name, 'w')
    self.config.write(configfile)
    configfile.close()

    print("date=%s time=%s file=%s ex=%d iso=%d br=%s sat=%d co=%d img_brightness=%d sleep=%0.1f speed=%d" % (now.strftime("%Y-%m-%d"), now.strftime("%H:%M:%S"), fullname, imgExpTime, camera.iso, camera.brightness, camera.saturation, camera.contrast, imgBrightness, diff, camera.shutter_speed))

    # start converter process
    self.convert_image(now, fullname)


def settle(self, duration):
    print("Settle capturing... waiting for %0.1fs" % (duration / 1000000))
    sleep (int(duration / 1000000))


def start(self, interval):
    # initial configuration
    sensor_mode = self.config.getint('Camera', 'SensorMode')
    resolution = (self.config.getint('Camera', 'resX'),
                  self.config.getint('Camera', 'resY'))

    # manually set the timeout, necessary for long exposures
    PiCamera.CAPTURE_TIMEOUT = self.config.getint('Camera', 'ExposureTimeout')
    # derive the frame rate range from longest exposure
    fr = (Fraction(1, int(self.config.getint('Night', 'MaxExposure')/1000000)), Fraction(1, 1))

    # shoot images
    with PiCamera(framerate_range=fr, resolution=resolution,
                  sensor_mode=sensor_mode) as camera:
        while True:
            # abort if stop file exists
            if self.stopfile.is_file():
                print ("Stopping timelapse")
                break
            else:
                # single shot
                self.single_shot(interval, camera)

        # sleeping a while until everything is settled
        self.settle(1.5*camera.exposure_speed)

    if self.stopfile.is_file():
        # we are finished
        os.remove(self.stopfile)

if name == "main":
service = TimelapseService()
service.start(service.config.getint('Camera', 'interval')) `

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

1 participant