jim-easterbrook / python-gphoto2

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

Capture multiple RAW+JPG and download the JPGs #51

Closed Yawgmoth90 closed 6 years ago

Yawgmoth90 commented 6 years ago

Hi, I'm trying to use this library for camera control in a Raspberry Pi-controlled timelapse slider.

My setup is: Raspberry Pi Zero W, running Raspbian Stretch libgphoto2/2.5.15 python-gphoto2-1.8.2 Canon EOS 6D

I need to shoot in "RAW+JPG" mode and to download only the JPG to the RPi (for bulb ramping). Since camera.capture returns the file_path of the RAW file (extension .CR2), I replace the extension with ".JPG" to call camera.file_get. However, this only works on the first shot, while the second call results in a gphoto2.GPhoto2Error: [-108] File not found, whereas the file actually exists in the same path. If I try to get the .CR2 file instead it works correctly.

Code to reproduce the behavior:

import gphoto2 as gp
import logging
import os

#Logging
logging.basicConfig(format='%(levelname)s: %(name)s: %(message)s', level=logging.WARNING)
gp.check_result(gp.use_python_logging())

#Camera init
camera = gp.Camera()
camera.init()

for i in range(2):
    #Capture image RAW+JPG
    file_path=camera.capture(gp.GP_CAPTURE_IMAGE)
    print("Captured " + file_path.folder + "/" + file_path.name)
    #Save JPG to Raspi /tmp
    file_path_jpg = file_path.name.replace("CR2", "JPG")
    camera_file = camera.file_get(file_path.folder,file_path_jpg,gp.GP_FILE_TYPE_NORMAL)
    target = os.path.join("/tmp",file_path_jpg)
    print("Copying to: " + target)
    gp.gp_file_save(camera_file, target)

camera.exit()

And the resulting output:

WARNING: gphoto2: (ptp_list_folder_eos [library.c:7342]) storage 0xffffffff, but handle 0x00000000?
Captured /store_00020001/DCIM/100CANON/IMG_8849.CR2
Copying to: /tmp/IMG_8849.JPG
Captured /store_00020001/DCIM/100CANON/IMG_8850.CR2
WARNING: gphoto2: (gp_camera_file_get [gphoto2-camera.c:1693]) 'gp_filesystem_get_file (camera->fs, folder, file, type, camera_file, context)' failed: -108
Traceback (most recent call last):
  File "minimal.py", line 19, in <module>
    camera_file = camera.file_get(file_path.folder,file_path_jpg,gp.GP_FILE_TYPE_NORMAL)
gphoto2.GPhoto2Error: [-108] File not found

The only solution I have found at the moment is to call camera.exit() after each shot, but it does not seem ideal.

Thanks! Alessio

jim-easterbrook commented 6 years ago

Are you allowing enough time for the camera to save the JPEG file? Try adding a few seconds sleep to your loop. (If it solves the problem you can then reduce the sleep time.)

Yawgmoth90 commented 6 years ago

Yeah! I already tried inserting sleeps up to 10 seconds, with no success.

jim-easterbrook commented 6 years ago

In that case I don't know what to suggest. (I don't think it matters, but I wonder if it would make any difference if you created a context and passed it to each function? I don't understand what "context" is for and why it's optional.)

I can't believe this is specifically a python-gphoto2 problem. It might be interesting to try writing an equivalent C program, using libgphoto2 directly, and see how its behaviour differs.

PS I'm about to go out for the evening, so I won't be able to respond immediately.

Yawgmoth90 commented 6 years ago

Passing the context didn't help, nor did using another pc with Arch and libgphoto2.5.16. I agree it's probably a libgphoto problem, so I'll just stick with the camera.exit() workaround. Thank you for the answers!

jim-easterbrook commented 6 years ago

Does it work OK if you transfer the CR2 file instead of the JPG? My Canon 100D embeds JPGs (full resolution but coarser quantisation) in the CR2 file - I don't know if your 6D does the same and if you could use them instead.

jim-easterbrook commented 6 years ago

One more thought. As it works if you re-initialise the camera that suggests something needs to be freed or released between captures. The Python gphoto2 objects only release their C counterparts when the Python object is deleted or reassigned. You could try adding a couple of del statements to your loop as follows:

import gphoto2 as gp
import os

camera = gp.Camera()
camera.init()

for i in range(2):
    file_path = camera.capture(gp.GP_CAPTURE_IMAGE)
    file_path_jpg = file_path.name.replace("CR2", "JPG")
    camera_file = camera.file_get(file_path.folder, file_path_jpg, gp.GP_FILE_TYPE_NORMAL)
    del file_path
    target = os.path.join("/tmp", file_path_jpg)
    camera_file.save(target)
    del camera_file

camera.exit()
Yawgmoth90 commented 6 years ago

Got the same result even when deleting the Python objects. Unfortunately I can't just copy the CR2 and get the full-res JPG because I need to minimize the delay between shots (that includes computing the histogram). Hence the best option would be to shoot raw+small jpg(720x480), then copy the jpg to the RPi and use it for the histogram.

jim-easterbrook commented 6 years ago

Eureka! ("I have found it!")

A bit of Googling found a message to the gphoto2-devel mailing list that describes a very similar problem. See https://sourceforge.net/p/gphoto/mailman/message/11167963/

An extract: "The list of files in the camera folder never updates. Or rather, it updates once after the first image capture because the folder is initialized with flag files_dirty=1, triggering an update in function gp_filesystem_number (libgphoto2/gphoto2-filesys.c). The update sets files_dirty=0 and then the file list never updates again, causing failure because the next image can't be found."

This would cause exactly your problem - and reinitialising the camera no doubt fetches the updated list.

Yawgmoth90 commented 6 years ago

Great, thanks for the explanation! I am closing the issue here at this point :)

maxholgasson commented 5 years ago

Great, thanks for the explanation! I am closing the issue here at this point :)

Hey, would you share your working code?

jim-easterbrook commented 5 years ago

I don't know if anyone has this working yet. That will depend on the bug in libgphoto2 being fixed.