process_pipeline.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. #include "process_pipeline.h"
  2. #include "pipeline.h"
  3. #include "main.h"
  4. #include "config.h"
  5. #include "quickpreview.h"
  6. #include <tiffio.h>
  7. #include <assert.h>
  8. #include <math.h>
  9. #include <wordexp.h>
  10. #include <gtk/gtk.h>
  11. #define TIFFTAG_FORWARDMATRIX1 50964
  12. static const float colormatrix_srgb[] = {
  13. 3.2409, -1.5373, -0.4986,
  14. -0.9692, 1.8759, 0.0415,
  15. 0.0556, -0.2039, 1.0569
  16. };
  17. static MPPipeline *pipeline;
  18. static char burst_dir[23];
  19. static char processing_script[512];
  20. static volatile bool is_capturing = false;
  21. static volatile int frames_processed = 0;
  22. static volatile int frames_received = 0;
  23. static const struct mp_camera_config *camera;
  24. static MPCameraMode mode;
  25. static int burst_length;
  26. static int captures_remaining = 0;
  27. static int preview_width;
  28. static int preview_height;
  29. // static bool gain_is_manual;
  30. static int gain;
  31. static int gain_max;
  32. static bool exposure_is_manual;
  33. static int exposure;
  34. static char capture_fname[255];
  35. // static void
  36. // process_image(const int *p, int size)
  37. // {
  38. // time_t rawtime;
  39. // char datetime[20] = {0};
  40. // struct tm tim;
  41. // uint8_t *pixels;
  42. // char fname[255];
  43. // char fname_target[255];
  44. // char command[1024];
  45. // char timestamp[30];
  46. // char uniquecameramodel[255];
  47. // GdkPixbuf *pixbuf;
  48. // GdkPixbuf *pixbufrot;
  49. // GdkPixbuf *thumb;
  50. // GError *error = NULL;
  51. // double scale;
  52. // cairo_t *cr;
  53. // TIFF *tif;
  54. // int skip = 2;
  55. // long sub_offset = 0;
  56. // uint64 exif_offset = 0;
  57. // static const short cfapatterndim[] = {2, 2};
  58. // static const float neutral[] = {1.0, 1.0, 1.0};
  59. // static uint16_t isospeed[] = {0};
  60. // // Only process preview frames when not capturing
  61. // if (capture == 0) {
  62. // } else {
  63. // if (capture == 0) {
  64. // // Restore the auto exposure and gain if needed
  65. // // if (auto_exposure) {
  66. // // v4l2_ctrl_set(current.fd, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_AUTO);
  67. // // }
  68. // // if (auto_gain) {
  69. // // v4l2_ctrl_set(current.fd, V4L2_CID_AUTOGAIN, 1);
  70. // // }
  71. // }
  72. // }
  73. // }
  74. static void
  75. register_custom_tiff_tags(TIFF *tif)
  76. {
  77. static const TIFFFieldInfo custom_fields[] = {
  78. {TIFFTAG_FORWARDMATRIX1, -1, -1, TIFF_SRATIONAL, FIELD_CUSTOM, 1, 1, "ForwardMatrix1"},
  79. };
  80. // Add missing dng fields
  81. TIFFMergeFieldInfo(tif, custom_fields, sizeof(custom_fields) / sizeof(custom_fields[0]));
  82. }
  83. static bool
  84. find_processor(char *script)
  85. {
  86. char *xdg_config_home;
  87. char filename[] = "postprocess.sh";
  88. wordexp_t exp_result;
  89. // Resolve XDG stuff
  90. if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
  91. xdg_config_home = "~/.config";
  92. }
  93. wordexp(xdg_config_home, &exp_result, 0);
  94. xdg_config_home = strdup(exp_result.we_wordv[0]);
  95. wordfree(&exp_result);
  96. // Check postprocess.h in the current working directory
  97. sprintf(script, "%s", filename);
  98. if(access(script, F_OK) != -1) {
  99. sprintf(script, "./%s", filename);
  100. printf("Found postprocessor script at %s\n", script);
  101. return true;
  102. }
  103. // Check for a script in XDG_CONFIG_HOME
  104. sprintf(script, "%s/megapixels/%s", xdg_config_home, filename);
  105. if(access(script, F_OK) != -1) {
  106. printf("Found postprocessor script at %s\n", script);
  107. return true;
  108. }
  109. // Check user overridden /etc/megapixels/postprocessor.sh
  110. sprintf(script, "%s/megapixels/%s", SYSCONFDIR, filename);
  111. if(access(script, F_OK) != -1) {
  112. printf("Found postprocessor script at %s\n", script);
  113. return true;
  114. }
  115. // Check packaged /usr/share/megapixels/postprocessor.sh
  116. sprintf(script, "%s/megapixels/%s", DATADIR, filename);
  117. if(access(script, F_OK) != -1) {
  118. printf("Found postprocessor script at %s\n", script);
  119. return true;
  120. }
  121. return false;
  122. }
  123. static void setup(MPPipeline *pipeline, const void *data)
  124. {
  125. TIFFSetTagExtender(register_custom_tiff_tags);
  126. if (!find_processor(processing_script)) {
  127. g_printerr("Could not find any post-process script\n");
  128. exit(1);
  129. }
  130. }
  131. void mp_process_pipeline_start()
  132. {
  133. pipeline = mp_pipeline_new();
  134. mp_pipeline_invoke(pipeline, setup, NULL, 0);
  135. }
  136. void mp_process_pipeline_stop()
  137. {
  138. mp_pipeline_free(pipeline);
  139. }
  140. static void
  141. process_image_for_preview(const MPImage *image)
  142. {
  143. uint32_t surface_width, surface_height, skip;
  144. quick_preview_size(
  145. &surface_width,
  146. &surface_height,
  147. &skip,
  148. preview_width,
  149. preview_height,
  150. image->width,
  151. image->height,
  152. image->pixel_format,
  153. camera->rotate);
  154. cairo_surface_t *surface = cairo_image_surface_create(
  155. CAIRO_FORMAT_RGB24,
  156. surface_width,
  157. surface_height);
  158. uint8_t *pixels = cairo_image_surface_get_data(surface);
  159. quick_preview(
  160. (uint32_t *)pixels,
  161. surface_width,
  162. surface_height,
  163. image->data,
  164. image->width,
  165. image->height,
  166. image->pixel_format,
  167. camera->rotate,
  168. camera->mirrored,
  169. camera->colormatrix[0] == 0 ? NULL : camera->colormatrix,
  170. camera->blacklevel,
  171. skip);
  172. mp_main_set_preview(surface);
  173. }
  174. static void
  175. process_image_for_capture(const MPImage *image, int count)
  176. {
  177. time_t rawtime;
  178. time(&rawtime);
  179. struct tm tim = *(localtime(&rawtime));
  180. char datetime[20] = {0};
  181. strftime(datetime, 20, "%Y:%m:%d %H:%M:%S", &tim);
  182. char fname[255];
  183. sprintf(fname, "%s/%d.dng", burst_dir, count);
  184. TIFF *tif = TIFFOpen(fname, "w");
  185. if(!tif) {
  186. printf("Could not open tiff\n");
  187. }
  188. // Define TIFF thumbnail
  189. TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1);
  190. TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, image->width >> 4);
  191. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, image->height >> 4);
  192. TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
  193. TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
  194. TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
  195. TIFFSetField(tif, TIFFTAG_MAKE, mp_get_device_make());
  196. TIFFSetField(tif, TIFFTAG_MODEL, mp_get_device_model());
  197. uint16_t orientation;
  198. if (camera->rotate == 0) {
  199. orientation = camera->mirrored ? ORIENTATION_TOPRIGHT : ORIENTATION_TOPLEFT;
  200. } else if (camera->rotate == 90) {
  201. orientation = camera->mirrored ? ORIENTATION_RIGHTBOT : ORIENTATION_LEFTBOT;
  202. } else if (camera->rotate == 180) {
  203. orientation = camera->mirrored ? ORIENTATION_BOTLEFT : ORIENTATION_BOTRIGHT;
  204. } else {
  205. orientation = camera->mirrored ? ORIENTATION_LEFTTOP : ORIENTATION_RIGHTTOP;
  206. }
  207. TIFFSetField(tif, TIFFTAG_ORIENTATION, orientation);
  208. TIFFSetField(tif, TIFFTAG_DATETIME, datetime);
  209. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
  210. TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  211. TIFFSetField(tif, TIFFTAG_SOFTWARE, "Megapixels");
  212. long sub_offset = 0;
  213. TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &sub_offset);
  214. TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0");
  215. TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0");
  216. char uniquecameramodel[255];
  217. sprintf(uniquecameramodel, "%s %s", mp_get_device_make(), mp_get_device_model());
  218. TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, uniquecameramodel);
  219. if(camera->colormatrix[0]) {
  220. TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, camera->colormatrix);
  221. } else {
  222. TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colormatrix_srgb);
  223. }
  224. if(camera->forwardmatrix[0]) {
  225. TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9, camera->forwardmatrix);
  226. }
  227. static const float neutral[] = {1.0, 1.0, 1.0};
  228. TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
  229. TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
  230. // Write black thumbnail, only windows uses this
  231. {
  232. unsigned char *buf = (unsigned char *)calloc(1, (int)image->width >> 4);
  233. for (int row = 0; row < image->height>>4; row++) {
  234. TIFFWriteScanline(tif, buf, row, 0);
  235. }
  236. free(buf);
  237. }
  238. TIFFWriteDirectory(tif);
  239. // Define main photo
  240. TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
  241. TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, image->width);
  242. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, image->height);
  243. TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
  244. TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
  245. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
  246. TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  247. static const short cfapatterndim[] = {2, 2};
  248. TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfapatterndim);
  249. TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\002\001\001\000"); // BGGR
  250. if(camera->whitelevel) {
  251. TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &camera->whitelevel);
  252. }
  253. if(camera->blacklevel) {
  254. TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 1, &camera->blacklevel);
  255. }
  256. TIFFCheckpointDirectory(tif);
  257. printf("Writing frame to %s\n", fname);
  258. unsigned char *pLine = (unsigned char*)malloc(image->width);
  259. for(int row = 0; row < image->height; row++){
  260. TIFFWriteScanline(tif, image->data + (row * image->width), row, 0);
  261. }
  262. free(pLine);
  263. TIFFWriteDirectory(tif);
  264. // Add an EXIF block to the tiff
  265. TIFFCreateEXIFDirectory(tif);
  266. // 1 = manual, 2 = full auto, 3 = aperture priority, 4 = shutter priority
  267. if (!exposure_is_manual) {
  268. TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 2);
  269. } else {
  270. TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 1);
  271. }
  272. TIFFSetField(tif, EXIFTAG_EXPOSURETIME, (mode.frame_interval.numerator / (float)mode.frame_interval.denominator) / ((float)image->height / (float)exposure));
  273. uint16_t isospeed[1];
  274. isospeed[0] = (uint16_t)remap(gain - 1, 0, gain_max, camera->iso_min, camera->iso_max);
  275. TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, isospeed);
  276. TIFFSetField(tif, EXIFTAG_FLASH, 0);
  277. TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime);
  278. TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime);
  279. if(camera->fnumber) {
  280. TIFFSetField(tif, EXIFTAG_FNUMBER, camera->fnumber);
  281. }
  282. if(camera->focallength) {
  283. TIFFSetField(tif, EXIFTAG_FOCALLENGTH, camera->focallength);
  284. }
  285. if(camera->focallength && camera->cropfactor) {
  286. TIFFSetField(tif, EXIFTAG_FOCALLENGTHIN35MMFILM, (short)(camera->focallength * camera->cropfactor));
  287. }
  288. uint64_t exif_offset = 0;
  289. TIFFWriteCustomDirectory(tif, &exif_offset);
  290. TIFFFreeDirectory(tif);
  291. // Update exif pointer
  292. TIFFSetDirectory(tif, 0);
  293. TIFFSetField(tif, TIFFTAG_EXIFIFD, exif_offset);
  294. TIFFRewriteDirectory(tif);
  295. TIFFClose(tif);
  296. }
  297. static void
  298. process_capture_burst()
  299. {
  300. time_t rawtime;
  301. time(&rawtime);
  302. struct tm tim = *(localtime(&rawtime));
  303. char timestamp[30];
  304. strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
  305. sprintf(capture_fname, "%s/Pictures/IMG%s", getenv("HOME"), timestamp);
  306. // Start post-processing the captured burst
  307. g_print("Post process %s to %s.ext\n", burst_dir, capture_fname);
  308. char command[1024];
  309. sprintf(command, "%s %s %s &", processing_script, burst_dir, capture_fname);
  310. system(command);
  311. }
  312. static void
  313. process_image(MPPipeline *pipeline, const MPImage *image)
  314. {
  315. assert(image->width == mode.width && image->height == mode.height);
  316. process_image_for_preview(image);
  317. if (captures_remaining > 0) {
  318. int count = burst_length - captures_remaining;
  319. --captures_remaining;
  320. process_image_for_capture(image, count);
  321. if (captures_remaining == 0) {
  322. process_capture_burst();
  323. mp_main_capture_completed(capture_fname);
  324. }
  325. }
  326. free(image->data);
  327. ++frames_processed;
  328. if (captures_remaining == 0) {
  329. is_capturing = false;
  330. }
  331. }
  332. void mp_process_pipeline_process_image(MPImage image)
  333. {
  334. // If we haven't processed the previous frame yet, drop this one
  335. if (frames_received != frames_processed && !is_capturing) {
  336. printf("Dropped frame at capture %d %d\n", frames_received, frames_processed);
  337. return;
  338. }
  339. ++frames_received;
  340. mp_pipeline_invoke(pipeline, (MPPipelineCallback)process_image, &image, sizeof(MPImage));
  341. }
  342. static void capture()
  343. {
  344. char template[] = "/tmp/megapixels.XXXXXX";
  345. char *tempdir;
  346. tempdir = mkdtemp(template);
  347. if (tempdir == NULL) {
  348. g_printerr("Could not make capture directory %s\n", template);
  349. exit (EXIT_FAILURE);
  350. }
  351. strcpy(burst_dir, tempdir);
  352. captures_remaining = burst_length;
  353. }
  354. void mp_process_pipeline_capture()
  355. {
  356. is_capturing = true;
  357. mp_pipeline_invoke(pipeline, capture, NULL, 0);
  358. }
  359. static void
  360. update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state)
  361. {
  362. camera = state->camera;
  363. mode = state->mode;
  364. burst_length = state->burst_length;
  365. preview_width = state->preview_width;
  366. preview_height = state->preview_height;
  367. // gain_is_manual = state->gain_is_manual;
  368. gain = state->gain;
  369. gain_max = state->gain_max;
  370. exposure_is_manual = state->exposure_is_manual;
  371. exposure = state->exposure;
  372. struct mp_main_state main_state = {
  373. .camera = camera,
  374. .mode = mode,
  375. .gain_is_manual = state->gain_is_manual,
  376. .gain = gain,
  377. .gain_max = gain_max,
  378. .exposure_is_manual = exposure_is_manual,
  379. .exposure = exposure,
  380. .has_auto_focus_continuous = state->has_auto_focus_continuous,
  381. .has_auto_focus_start = state->has_auto_focus_start,
  382. };
  383. mp_main_update_state(&main_state);
  384. }
  385. void mp_process_pipeline_update_state(const struct mp_process_pipeline_state *new_state)
  386. {
  387. mp_pipeline_invoke(pipeline, (MPPipelineCallback)update_state, new_state, sizeof(struct mp_process_pipeline_state));
  388. }