Remote Capture Canon XTi
I have been trying to get my Canon Digital Rebel XTi (400D) to repeatedly capture images via computer generated triggers (remote capture). After much fiddling with the official (Windows only) Canon SDK, I gave up on the Windows messaging requirements, since I am looking for purely console-based applications that will work well with OpenGL.
This led me to the libgphoto2 API. After some tweaking and an excellent post on the devel messageboard. I got the camera to respond well to the API calls.
This left me dealing with JPEG images referenced as FILE pointers, so I decided to include some libjpeg calls to decompress the JPEG stream in memory instead of the added file I/O overhead.
The code provided below uses openCV to convert BGR-:RGB and display images. If anyone tries this code and has suggestions feel free to comment. I have not fully tested it, so it may contain bugs. It should compile with something like: "g++ cvtest.cpp -o cvtest -L/usr/lib/ -lcxcore -lcv -lhighgui -L/usr/local/lib -lgphoto2 -lgphoto2_port -lm"
// Capture and Display code for Canon Digital Rebel XTi (7/31/08)
//
// Much of the gphoto code came from a post on the gphoto-devel mailinglist
// http://sourceforge.net/mailarchive/message.php?msg_name=Pine.LNX.4.64.0807211838060.7583%40pl2.zayda.com
// The web archives do not list the poster's information, but thanks!
//
// The code for using a custom source manager in libjpeg was modified from http://wiki.allegro.cc/Libjpeg
//
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <jpeglib.h>
#include <jerror.h>
#include "opencv/cv.h"
#include "opencv/cxcore.h"
#include "opencv/highgui.h"
extern "C"{
#include <gphoto2/gphoto2-abilities-list.h>
#include <gphoto2/gphoto2-camera.h>
}
#define GPHOTO_ERROR(x,y) fprintf (stderr, "%s: '%s'.\n", x, gp_result_as_string (y))
typedef struct my_src_mgr my_src_mgr;
struct my_src_mgr{
struct jpeg_source_mgr pub;
JOCTET eoi_buffer[2];
};
static void init_source(j_decompress_ptr cinfo){
}
static int fill_input_buffer(j_decompress_ptr cinfo){
return 1;
}
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes){
my_src_mgr *src = (my_src_mgr *)cinfo->src;
if (num_bytes > 0) {
while (num_bytes > (long)src->pub.bytes_in_buffer){
num_bytes -= (long)src->pub.bytes_in_buffer;
fill_input_buffer(cinfo);
}
}
src->pub.next_input_byte += num_bytes;
src->pub.bytes_in_buffer -= num_bytes;
}
static void term_source(j_decompress_ptr cinfo){
}
void jpeg_memory_src(j_decompress_ptr cinfo, unsigned char const *buffer, size_t bufsize){
my_src_mgr *src;
if (! cinfo->src){
cinfo->src = (jpeg_source_mgr*)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(my_src_mgr));
}
src = (my_src_mgr *)cinfo->src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart;
src->pub.term_source = term_source;
src->pub.next_input_byte = buffer;
src->pub.bytes_in_buffer = bufsize;
}
int main(){
IplImage * imgBGR, * imgRGB;
cvNamedWindow("test", 0);
int i =0;
int retval;
int key = -1;
CameraFilePath path;
CameraFile *file = NULL;
CameraFileInfo info;
Camera *camera = NULL;
unsigned long size;
const unsigned char *data;
// If true this will set up memory allocation for image data structure (IplImage)
bool firstCapture = true;
//libjpeg variabless
char * raw_image= NULL;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
//libjpeg data structure for storing one row, that is, scanline of an image
JSAMPROW row_pointer[1];
unsigned long location = 0;
//Variables for setting gphoto to "capture"
CameraWidget *rootconfig;
CameraWidget *actualrootconfig;
CameraWidget *child;
const char *widgetinfo;
const char *widgetlabel;
int widgetid;
CameraWidgetType widgettype;
CameraWidget *capture = NULL;
//Does 0 save to internal memory and 1 save to CF?
int captureValue = 1;
// Capture an image, download it and delete it
// Initializing camera
if((retval = gp_camera_new (&camera)) != GP_OK)
GPHOTO_ERROR("New Camera", retval);
if((retval = gp_camera_init(camera, NULL)) != GP_OK)
GPHOTO_ERROR("Camera Init", retval);
//Get root config
if((retval = gp_camera_get_config(camera, &rootconfig, NULL)) != GP_OK)
GPHOTO_ERROR("Get Config", retval);
actualrootconfig = rootconfig;
//Get main config
if((retval = gp_widget_get_child_by_name(rootconfig, "main", &child)) != GP_OK)
GPHOTO_ERROR("Get Child - Main", retval);
//Get settings config
rootconfig = child;
if((retval = gp_widget_get_child_by_name(rootconfig, "settings", &child)) != GP_OK)
GPHOTO_ERROR("Get Child - Settings", retval);
//Get capture config
rootconfig = child;
if((retval = gp_widget_get_child_by_name(rootconfig, "capture", &child)) != GP_OK)
GPHOTO_ERROR("Get Chile - Capture", retval);
capture = child;
gp_widget_get_name(capture, &widgetinfo);
gp_widget_get_label(capture, &widgetlabel);
gp_widget_get_id(capture, &widgetid);
gp_widget_get_type(capture, &widgettype);
//Set value for save location internal memory or CF
if((retval = gp_widget_set_value(capture, &captureValue)) != GP_OK)
GPHOTO_ERROR("Set capture location", retval);
//Enabling capture
if((retval = gp_camera_set_config(camera, actualrootconfig, NULL)) != GP_OK)
GPHOTO_ERROR("Enable capture with config", retval);
//Begin continuous image capture / JPEG decompression / display image
while(key != 'q'){
// NOP: This gets overridden in the library to /capt0000.jpg
strcpy(path.folder, "/store_5");
strcpy(path.name, "foo.jpg");
//Capturing image
if((retval=gp_camera_capture (camera, GP_CAPTURE_IMAGE, &path, NULL)) != GP_OK)
GPHOTO_ERROR("Capture Image",retval);
if((retval = gp_file_new (&file)) != GP_OK)
GPHOTO_ERROR("Create new file", retval);
//Downloading image
if((retval= gp_camera_file_get (camera, path.folder, path.name, GP_FILE_TYPE_NORMAL, file, NULL))!= GP_OK)
GPHOTO_ERROR("Retrieve file",retval);
//Getting Info
if((retval=gp_camera_file_get_info(camera, path.folder, path.name, &info, NULL)) != GP_OK)
GPHOTO_ERROR("Retrieve file info",retval);
if((retval=gp_file_get_data_and_size (file, (const char**)&data, &size)) != GP_OK)
GPHOTO_ERROR("Retrieve file data and size", retval);
location = 0;
i = 0;
// here we set up the standard libjpeg error handler
cinfo.err = jpeg_std_error( &jerr );
// setup decompression process and source, then read JPEG header
jpeg_create_decompress( &cinfo );
// setup source for memory read
jpeg_memory_src(&cinfo, data, size);
//reading the image header which contains image information
jpeg_read_header( &cinfo, TRUE );
//Uncomment the following to output image information, if needed.
//printf( "JPEG File Information: \n" );
//printf( "Image width and height: %d pixels and %d pixels.\n", cinfo.image_width, cinfo.image_height );
//printf( "Color components per pixel: %d.\n", cinfo.num_components );
//printf( "Color space: %d.\n", cinfo.jpeg_color_space );
//Start decompression jpeg here
jpeg_start_decompress( &cinfo );
if(firstCapture){
imgBGR =cvCreateImageHeader(cvSize( cinfo.output_width,cinfo.output_height), IPL_DEPTH_8U, cinfo.num_components);
cvCreateData(imgBGR);
imgRGB =cvCreateImageHeader(cvSize( cinfo.output_width,cinfo.output_height), IPL_DEPTH_8U, cinfo.num_components);
cvCreateData(imgRGB);
firstCapture = false;
}
//allocate memory to hold the uncompressed image
raw_image = imgBGR->imageData;
//now actually read the jpeg into the raw buffer
row_pointer[0] = (unsigned char *)malloc( cinfo.output_width*cinfo.num_components );
//read one scan line at a time
while( cinfo.output_scanline < cinfo.image_height ){
jpeg_read_scanlines( &cinfo, row_pointer, 1 );
for( i=0; i<cinfo.image_width*cinfo.num_components;i++){
raw_image[location++] = row_pointer[0][i];
}
}
//wrap up decompression, destroy objects, free pointers
jpeg_finish_decompress( &cinfo );
jpeg_destroy_decompress( &cinfo );
free( row_pointer[0] );
//Deleting image
gp_camera_file_delete (camera, path.folder, path.name, NULL);
//Show image with openCV window and wait for key (q quits loop)
cvCvtColor(imgBGR, imgRGB, CV_BGR2RGB);
cvShowImage("test", imgRGB);
key = cvWaitKey();
}
//Finish up with gphoto2
if((retval=gp_camera_exit(camera, NULL)) != GP_OK)
GPHOTO_ERROR("Exit Camera", retval);
return 0;
}