interactionresearchstudio / NaturewatchCameraServer

A Python / OpenCV camera server to stream Pi camera content to a remote client through a website.
https://mynaturewatch.net
GNU General Public License v3.0
103 stars 49 forks source link

Exposure slider jumps to the left when it's moved #47

Closed mikevanis closed 2 years ago

ieuan commented 3 years ago

I think this is related to https://github.com/waveform80/picamera/issues/522 where the stored shutter speed value is different to the returned value from the picamera object.

Vincent-Stragier commented 2 years ago

I think this is related to waveform80/picamera#522 where the stored shutter speed value is different to the returned value from the picamera object.

Yes, that's the issue. And since in ExposureSetting.js there is a dictionary that is used to set the slider position, it bugs. One fix could be to take the closest value compared to this dictionary instead of sending the real value. And thus, you could apply the following fix to api.py:

# Maybe just build a list or find a way to link
# the dictionary in ExposureSetting.js
possible_shutter_speeds = {
    "1/30": 33333,
    "1/40": 25000,
    "1/50": 20000,
    "1/60": 16666,
    "1/80": 12500,
    "1/100": 10000,
    "1/125": 8000,
    "1/160": 6250,
    "1/200": 5000,
    "1/250": 4000,
    "1/320": 3125,
    "1/400": 2500,
    "1/500": 2000,
    "1/640": 1563,
    "1/800": 1250,
    "1/1000": 1000,
    "1/1250": 800,
    "1/1600": 625,
    "1/2000": 500,
    "1/2500": 400,
    "1/3200": 313,
    "1/4000": 250
}
possible_shutter_speed_values = list(possible_shutter_speeds.values())

def closest(values, value):
    """Return the closest value in the values compared to value."""
    def key(index):
        return abs(values[index]-value)
    return values[min(range(len(values)), key=key)]

def construct_settings_object(camera_controller, change_detector):
    """
    Construct a dictionary populated with the current settings
    of the camera controller and change detector.
    :param camera_controller: Running camera controller object
    :param change_detector: Running change detector object
    :return: settings dictionary
    """

    sensitivity = "default"
    if change_detector.minWidth == current_app.user_config["less_sensitivity"]:
        sensitivity = "less"
    elif change_detector.minWidth == current_app.user_config["min_width"]:
        sensitivity = "default"
    elif change_detector.minWidth == current_app.user_config[
            "more_sensitivity"]:
        sensitivity = "more"

    shutter_speed = camera_controller.get_shutter_speed()
    shutter_speed = int(closest(possible_shutter_speed_values, shutter_speed))

    settings = {
        "rotation": camera_controller.rotated_camera,
        "exposure": {
            "mode": camera_controller.get_exposure_mode(),
            "iso": camera_controller.get_iso(),
            "shutter_speed": shutter_speed,
        },
        "sensitivity": sensitivity,
        "timelapse": {
            "active": current_app.change_detector.timelapse_active,
            "interval": current_app.change_detector.timelapse,
        }
    }
    return settings
Vincent-Stragier commented 2 years ago

After a bit of reflection, I think applying a similar fix in ExposureSetting.js is better (easier to maintain).

    // Inspired from https://www.codevscolor.com/javascript-nearest-number-in-array#method-3-using-sort-
    findClosest = (arr, num) => {
        if (arr == null) {
            return
        }
        return arr.sort((a, b) => Math.abs(b - num) - Math.abs(a - num)).pop();
    }

    getIndexFromShutterSpeed(shutterSpeed) {
        // Get nearest value
        shutterSpeed = this.findClosest(Object.values(this.cameraShutterSpeeds), shutterSpeed)

        for (var i = 0; i < Object.keys(this.cameraShutterSpeeds).length; i++) {
            if (Object.values(this.cameraShutterSpeeds)[i] === shutterSpeed) {
                return i;
            }
        }
        return 0;
    }