preview
preview的意思是预览,代码使用一个for循环快速采集100张图片并保存来展示preview的效果。而我希望的preview是将实时采集的图片通过界面的形式显示出来。preview区分一般采集图片的功能,主要是gp_camera_capture_preview()
和capture_to_file()
的区别,前者采集低分辨率的图片,后者采集高分辨率的图片,并且为了支持采集的模式,相机的采集模式也需要进行不同的调整。
preview代码流程
- 初始化相机、相机使能;
- 调用
camera_eosviewfinder()
函数,这个函数的功能有待探讨; - 3-6步是循环中实现的:每个循环同样要新建一个相机文件句柄;
- 并且每10次循环就自动对焦一次,也就是调用camera_auto_focus()函数;
- 然后调用gp_camera_capture_preview的函数进行采集图片;
- 将采集到的图片保存起来,并且释放相机文件句柄。
camera_eosviewfinder()
函数实现中调用了一个函数_lookup_widget()
函数,这个函数的第二个参数需要根据相机类型进行调整,第二个参数是个字符串,默认输入是“eosviewfinder”,刚开始调用的时候就老返回错误,然后我询问了libgphoto2的作者,作者告诉我可能需要根据相机的种类进行调整,将输入参数从“eosviewfinder”修改为“viewfinder”——感谢作者,请作者收下我的膝盖!
gp_camera_capture_preview()
函数
这个函数采集一个预览,这个预览不会保存在相机上,但是会被返回在一个支持的文件里。在作者提供的例程里,就是采集到了一个相机文件句柄中。类似于上述提到的,该函数对于相机的采集模式有要求,如果是一般的关闭闪光灯、微距等的采集一张图片的模式,该函数的调用会出错,具体表现为:相机会执行采集的指令,然后出现generic error,generic error是什么原因导致的以及怎么处理,对于没有明确开发手册说明,所以处理方式并不明确。而我最终解决的方式为:调整了数码相机的采集模式,修改oneshot模式为P,TV,AV等,我试验的P是可以的,支持了该函数的调用,终于将代码跑通,获得了低分辨率的图片。
例程中的代码+一些中文注释
Camera *canon;
int i, retval;
GPContext *canoncontext = sample_create_context();
//gp_log_add_func(GP_LOG_ERROR, errordumper, 0);
gp_camera_new(&canon);//新建相机对象
/* When I set GP_LOG_DEBUG instead of GP_LOG_ERROR above, I noticed that the
* init function seems to traverse the entire filesystem on the camera. This
* is partly why it takes so long.
* (Marcus: the ptp2 driver does this by default currently.)
*/
printf("Camera init. Takes about 10 seconds.\n");
retval = gp_camera_init(canon, canoncontext);//相机初始化
if (retval != GP_OK) {
printf(" Retval: %d\n", retval);
exit (1);
}
canon_enable_capture(canon, TRUE, canoncontext);//canon使能
retval = camera_eosviewfinder(canon,canoncontext,1);//viewfinder,这个功能暂时还不是很清楚
if (retval != GP_OK) {
fprintf(stderr,"camera_eosviewfinder(1): %d\n", retval);
exit(1);
}
/*set_capturetarget(canon, canoncontext);*/
printf("Taking 100 previews and saving them to snapshot-XXX.jpg ...\n");
for (i=0;i<100;i++) {//循环100次,采集小图片并保存
CameraFile *file;
char output_file[32];
fprintf(stderr,"preview %d\n", i);
retval = gp_file_new(&file);//新建相机文件句柄
if (retval != GP_OK) {
fprintf(stderr,"gp_file_new: %d\n", retval);
exit(1);
}
/* autofocus every 10 shots */
if (i%10 == 9) {
camera_auto_focus (canon, canoncontext, 1);
/* FIXME: wait a bit and/or poll events ? */
camera_auto_focus (canon, canoncontext, 0);
} else {
camera_manual_focus (canon, (i/10-5)/2, canoncontext);
}
#if 0 /* testcase for EOS zooming */
{
char buf[20];
if (i<10) set_config_value_string (canon, "eoszoom", "5", canoncontext);
sprintf(buf,"%d,%d",(i&0x1f)*64,(i>>5)*64);
fprintf(stderr, "%d - %s\n", i, buf);
set_config_value_string (canon, "eoszoomposition", buf, canoncontext);
}
#endif
retval = gp_camera_capture_preview(canon, file, canoncontext);//采集小分辨率的图片
if (retval != GP_OK) {
fprintf(stderr,"gp_camera_capture_preview(%d): %d\n", i, retval);
exit(1);
}
sprintf(output_file, "snapshot-%03d.jpg", i);
retval = gp_file_save(file, output_file);//将小分辨率的图片保存到output_file指定的位置
if (retval != GP_OK) {
fprintf(stderr,"gp_camera_capture_preview(%d): %d\n", i, retval);
exit(1);
}
gp_file_unref(file);
/*
sprintf(output_file, "image-%03d.jpg", i);
capture_to_file(canon, canoncontext, output_file);
*/
}
retval = camera_eosviewfinder(canon,canoncontext,0);
if (retval != GP_OK) {
fprintf(stderr,"camera_eosviewfinder(0): %d\n", retval);
exit(1);
}
sleep(10);
gp_camera_exit(canon, canoncontext);//退出相机
在界面中显示preview的结果
东西怎么折腾也不会坏掉——坏掉老师也不会让赔吧真想手动@包工头
实现思路:
- 将上述代码中
retval = gp_file_save(file, output_file);
的file
中的数据转到熟悉的OpenCV的数据结构Mat中; - 将Mat中的数据通过界面的形式展示出来。
将file中的数据转到Mat中
file是CameraFile指针,该数据结构的内部数据是私有的,所以不能直接访问,我也没兴趣去寻找该数据结构的友元函数了,因此我采取的策略是:依然按照参考代码中存储图片的方式,将图片保存到计算机中指定的位置,然后使用OpenCV的imread()
函数读到Mat中——咿呀呀,我真懒。
通过界面展示Mat中的数据
涉及到一些信号槽函数、相机句柄等全局变量,所以讲for循环中的代码放到一个槽函数中,将别的如:相机句柄的定义放到界面类中,相机句柄的初始化,canon相机的使能放到界面的构造函数中,将相机的释放放到界面的析构函数中。然后使用一个计时器timeout
函数作为信号以运行槽函数即可。显然这种思路成功实现了数码相机的liveview需求。
preview模式下capture_to_file(memory)可以吗?
在支持preview的P模式下,preview的过程中是可以运行capture_to_file
函数的。但是我个人总感觉对相机不太友好。出现了以下几种情况:
- preview过程中调用
capture_to_file
函数,相机反应相对比较慢,采集图片的速度也比较慢; - preview的过程中调用
capture_to_file
函数,相机不是很稳定,如果随意对相机进行操作,会导致调用capture_to_file
函数调用失败,可能是capture_to_file
函数有参数保存了相机的工作模式,一旦修改相机的模式,就与之前记录下的模式产生冲突,所以capture_to_file
函数运行会失败;但preview没有受到影响; - 相机在程序运行结束后,不会自动退出preview的状态,相机不再受手动操作,启动程序可以被程序接管。——对此我不知道相机发生了什么。
下一步
既然有办法可视化preview了,我就可以尝试在自己的主要界面上集成这个功能了,将双相机系统精简为单相机系统。需要特别注意,如果用不同的线程访问(或计时器触发)相机,该如何避免冲突。