jim-easterbrook / python-gphoto2

Python interface to libgphoto2
GNU Lesser General Public License v3.0
357 stars 59 forks source link

Bug: Configurations not fully set #117

Closed rwijtvliet closed 3 years ago

rwijtvliet commented 3 years ago

This page is for reporting problems with the Python interface to libgphoto2. If your question is about using libgphoto2 you should ask on the gphoto2 mailing list.

Your system What version of Python are you using? 3.9

What version of libgphoto2 have you installed? 2.5.25

How have you installed (or attempted to install) python-gphoto2? yes

Your problem I'm trying to assign configurations to variables, so that I can take a series of captures at a later point. The configurations are saved to variables OK, but the setting them with cam.set_config seems to not work in all cases.

# %% Setup

import gphoto2 as gp
from copy import copy, deepcopy

cam = gp.Camera()
cfg0 = cam.get_config()  # initial configuration as found in camera.

cfg1 = cam.get_config()  # ideally, `copy(cfg0)`, but "can't pickle CameraWidget object"
cfg1.get_child_by_name("imagesize").set_value("2992x2000")
cfg2 = cam.get_config()
cfg2.get_child_by_name("iso").set_value("25600")

# Let's convince ourselves that the configurations are correct:
print("".join([f"{header:>15}" for header in ["cfg", "imagesize", "iso"]]))
for i, cfg in enumerate([cfg0, cfg1, cfg2]):
    values = [
        f"{cfg.get_child_by_name(setting).get_value():>15}"
        for setting in ["imagesize", "iso"]
    ]
    print(f"{i:>15}" + "".join(values))
# cfg      imagesize            iso
#   0      6000x4000            400
#   1      2992x2000            400
#   2      6000x4000          25600

Note especially that cfg2 has imagesize set to 6000x4000. I now take a picture with each configuration:

def capture_and_save(cfg, path):
    cam.set_config(cfg)
    local = cam.capture(gp.GP_CAPTURE_IMAGE)
    file = cam.file_get(local.folder, local.name, gp.GP_FILE_TYPE_NORMAL)
    file.save(path)

capture_and_save(cfg0, "/home/ruud/img0.jpg")
capture_and_save(cfg1, "/home/ruud/img1.jpg")
capture_and_save(cfg2, "/home/ruud/img2.jpg")
cam.set_config(cfg0) # restore initial configuration
cam.disown()

The camera indeed snaps and saves 3 photos, but looking at the final one, its 'imagesize' is not '6000x4000' as specified:

image


Remarks:

Here is the output using the change suggested in the final remark: image

jim-easterbrook commented 3 years ago

I don't know enough about libgphoto2's config system to say if making multiple copies of the config tree like this would be expected to work. Are you sure this is a bug in the Python interface? What happens if you do the same operations in a C program?

rwijtvliet commented 3 years ago

Thanks for the reply Jim. I am not sure; I unfortunately don't have experience in C or an environment that I could use to check - but I agree it makes sense to check if the behaviour is present on the next level down before trying to fix it on this level.

I did play around with the command line interface but quickly thought it'd be easier to use your python package instead.

Let me know if there's anything I can help with; for the time being I have worked around the issue in the way I mentioned in my post.

jim-easterbrook commented 3 years ago

I don't know if gp_camera_get_config constructs a new CameraWidget tree each time it's called, or if it simply returns a pointer to the same tree. If the latter then what you are trying to do is bound to run into problems. I would try to avoid "copying" the config tree (not least because gp_camera_get_config takes a long time on some cameras), but keep a pointer to each config item of interest and set their values when required.

rwijtvliet commented 3 years ago

Regarding the variables pointing to the same object: that, I tested, is not the case and can also be seen when I print the imagesize and iso values which are distinct for each of the configurations cfg0, cfg1 and cfg2.

Your solution certainly works; I've gone a bit further and use the distinct configurations (as I had been doing) and a separate set of the settings names that don't have the same value across all. When setting a configuration to the camera, I no longer set it in its entirety, but one setting after the other I get the widget from the configuration in question (with .get_child_by_name) and set it to the camera (.set_single_config). That also seem to work and is easier in my use case (where only the changes to the original configuration are specified by each configuration).

jim-easterbrook commented 3 years ago

I'm glad you've got it working satisfactorily for your purposes.

rwijtvliet commented 3 years ago

Closing; found workaround