diff --git a/README b/README index 9edaf06..ced366c 100644 --- a/README +++ b/README @@ -9,6 +9,10 @@ to process their files. This tool supports both the large raw files that come from the Lytro camera and the compressed files that the desktop software produces for web display. +Note: The description below refers to .lfp file format for files generated +using Lytro's Version 1 processing software. See README_V2 for a description +of .lfp format changes as of Lytro's December 2012 update (Version 2). + .lfp file format ---------------- diff --git a/README_V2 b/README_V2 new file mode 100644 index 0000000..ad9e3c4 --- /dev/null +++ b/README_V2 @@ -0,0 +1,65 @@ +In December 2012 Lytro released a software upgrade that supports +perspective shift viewing and "living filters". This entailed a +change to the contents of .lfp files as described below. +================================================================ + +The Lytro Version 2 software generates three .lfp files instead of two +as in Version 1. These files are: + + 1. IMG_nnnn.lfp + + This file seems to be pretty much the same as Version 1. I have + not done a full analysis, however. + + 2. IMG_nnnn-dm.lfp + + This file is new in Version 2. It contains an expanded depth + map (330 x 330) as well as a confidence map of the same + dimensions. I will wait for someone else to explain what + the confidence map is used for. The depth map represents + lambda values, as in Version 1, but at a much finer + granularity than in Version 1. + + 3. IMG_nnnn-stk.lfp + + A file with this name was also generated by Lytro Version 1 software, + but with the Version 2 format the contents have changed. The most + significant difference is that the image stacks are encoded as H264 frame + sequences. The -stk.lfp file may contain one or two image stacks (H264 + frame sequences), depending on whether the user has performed + additional processing for shift perspective viewing using the + option provided in the Lytro processing software. + +An example of running the new lfpsplitter on Lytro files generated +with Lytro's version 2 software is shown below. + +$ ./lfpsplitter IMG_0008.lfp +Saved IMG_0008_table.json +Saved IMG_0008_imageRef0.raw +Saved IMG_0008_metadataRef0.json +Saved IMG_0008_privateMetadataRef1.json + +$ ./lfpsplitter IMG_0008-dm.lfp +Saved IMG_0008-dm_table.json +Saved IMG_0008-dm_lut_depth.txt +Saved IMG_0008-dm_lut_confidence.txt + +$ ./lfpsplitter IMG_0008-stk.lfp +Saved IMG_0008-stk_table.json +Saved IMG_0008-stk_blockOfImagesRef_00.h264 +Saved IMG_0008-stk_lut_depth.txt +Saved IMG_0008-stk_blockOfImagesRef_01.h264 + +The example shown above was run on an image where +perspective shift processing had been performed. If it +had not been performed, only one .h264 file would +have been output for IMG_0008-stk.lfp. + +Note tha lfpsplitter does NOT decode the H264 blocks into individual +images. There are legal issues swirling around H264 decoding, so you +will need to find another utility to accomplish this. You might +try ffmpeg (ffmpeg.org). Using that utility, you could try the +following command line to generate JPEGs: + +ffmpeg -format h265 -i IMG_0008-stk_blockOfImagesRef_00.h264 + -an -qscale 1 IMG_0008-stk_blockOfImagesRef_00_%04d.jpg diff --git a/lfpsplitter.c b/lfpsplitter.c index a0ae0a8..8ca3baa 100644 --- a/lfpsplitter.c +++ b/lfpsplitter.c @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Modifications for Lytro Version 2 files made on 1/13/2013 by + * Elissa Weidaw. */ #include @@ -31,6 +34,8 @@ typedef unsigned int uint32_t; #define MAGIC_LENGTH 12 #define BLANK_LENGTH 35 #define STRING_LENGTH 256 +#define LUT_DIM_V1 20 +#define LUT_DIM_V2 330 #ifdef _MSC_VER #define snprintf _snprintf @@ -205,13 +210,20 @@ static void lfp_identify_section(lfp_file_p lfp, lfp_section_p section) strcpy(section->name, "unknown"); } - // Hard coded to assume that the 20x20 LUT is 1600 bytes - if (section->len == 1600) { + // Test for Lytro Version 1 depth map + if (section->len == LUT_DIM_V1 * LUT_DIM_V1 * 4) { section->type = LFP_DEPTH_LUT; strcpy(section->name, "depth"); return; } + // Test for Lytro Version 2 LUT (depth or confidence) map + if (section->len == LUT_DIM_V2 * LUT_DIM_V2 * 4) { + section->type = LFP_LUT; + strcpy(section->name, "lut"); + return; + } + // Check for the magic bytes to see if its a jpg if ((section->len > sizeof(jpeg)) && (memcmp(section->data, jpeg, sizeof(jpeg)) == 0)) { @@ -219,8 +231,14 @@ static void lfp_identify_section(lfp_file_p lfp, lfp_section_p section) strcpy(section->name, "image"); return; } + + // Check for h264 block + if (strcmp(section->name, "blockOfImagesRef") == 0) { + section->type = LFP_BLOCK_OF_IMAGES; + return; + } - // Assume anything that isn't called imageRef is plain text json + // Assume anything else that isn't called imageRef is plain text json if (strcmp(section->name, "imageRef")) section->type = LFP_JSON; } @@ -256,7 +274,7 @@ static void lfp_save_sections(lfp_file_p lfp) { char name[STRING_LENGTH]; lfp_section_p section = lfp->sections; - int jpeg = 0, raw = 0, text = 0; + int jpeg = 0, raw = 0, text = 0, image_block = 0, lut = 0; char *buf; int buflen = 0; @@ -293,12 +311,36 @@ static void lfp_save_sections(lfp_file_p lfp) free(buf); } break; + + // Lytro Version 2 LUT - depth or confidence map + case LFP_LUT: + // Parse the LUT and save as plaintext + buf = depth_string(section->data, &buflen, section->len); + if (buf) { + if (lut++ == 0) { + snprintf(name, STRING_LENGTH, "%s_%s_depth.txt", lfp->filename, section->name); + } else { + snprintf(name, STRING_LENGTH, "%s_%s_confidence.txt", lfp->filename, section->name); + } + if (save_data(buf, buflen, name)){ + printf("Saved %s\n", name); + } + free(buf); + } + break; case LFP_JPEG: snprintf(name, STRING_LENGTH, "%s_%.2d.jpg", lfp->filename, jpeg++); if (save_data(section->data, section->len, name)) printf("Saved %s\n", name); break; + + // Lytro Version 2 H264 block + case LFP_BLOCK_OF_IMAGES: + snprintf(name, STRING_LENGTH, "%s_%s_%.2d.h264", lfp->filename, section->name, image_block++); + if (save_data(section->data, section->len, name)) + printf("Saved %s\n", name); + break; } section = section->next; diff --git a/lfpsplitter.h b/lfpsplitter.h index 8143aa1..9637a27 100644 --- a/lfpsplitter.h +++ b/lfpsplitter.h @@ -5,7 +5,9 @@ typedef enum { LFP_RAW_IMAGE, LFP_JSON, LFP_DEPTH_LUT, - LFP_JPEG + LFP_LUT, + LFP_JPEG, + LFP_BLOCK_OF_IMAGES } section_type; typedef struct lfp_section {