Write more metadata using libdng
This commit is contained in:
146
src/dcp.c
146
src/dcp.c
@@ -2,154 +2,8 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
unsigned int
|
|
||||||
get_int32(const unsigned char *buffer, size_t offset)
|
|
||||||
{
|
|
||||||
return (buffer[offset + 3] << 24) | (buffer[offset + 2] << 16) |
|
|
||||||
(buffer[offset + 1] << 8) | (buffer[offset]);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int
|
|
||||||
get_int16(const unsigned char *buffer, size_t offset)
|
|
||||||
{
|
|
||||||
return (buffer[offset + 1] << 8) | (buffer[offset]);
|
|
||||||
}
|
|
||||||
|
|
||||||
float
|
|
||||||
get_float(unsigned char *buffer, size_t offset)
|
|
||||||
{
|
|
||||||
float f;
|
|
||||||
unsigned char b[] = { buffer[offset + 0],
|
|
||||||
buffer[offset + 1],
|
|
||||||
buffer[offset + 2],
|
|
||||||
buffer[offset + 3] };
|
|
||||||
memcpy(&f, &b, sizeof(f));
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float
|
|
||||||
get_srational(unsigned char *buffer, size_t offset)
|
|
||||||
{
|
|
||||||
int a = (int)get_int32(buffer, offset);
|
|
||||||
int b = (int)get_int32(buffer, offset + 4);
|
|
||||||
return (float)a / (float)b;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MPCameraCalibration
|
|
||||||
parse_calibration_file(const char *path)
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
size_t size;
|
|
||||||
unsigned char *buffer;
|
|
||||||
|
|
||||||
struct MPCameraCalibration result = { 0 };
|
|
||||||
|
|
||||||
fp = fopen(path, "rb");
|
|
||||||
if (fp == NULL) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
|
||||||
size = ftell(fp);
|
|
||||||
fseek(fp, 0, SEEK_SET);
|
|
||||||
buffer = malloc(sizeof(char) * size);
|
|
||||||
size_t ret = fread(buffer, 1, size, fp);
|
|
||||||
if (ret != size) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
if (buffer[0] != 'I' || buffer[1] != 'I') {
|
|
||||||
fprintf(stderr, "Magic for DCP file incorrect\n");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (buffer[2] != 0x52 || buffer[3] != 0x43) {
|
|
||||||
fprintf(stderr, "Invalid DCP version\n");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int ifd0 = get_int32(buffer, 4);
|
|
||||||
unsigned int tag_count = get_int16(buffer, ifd0);
|
|
||||||
|
|
||||||
for (int i = 0; i < tag_count; i++) {
|
|
||||||
int tag_offset = ifd0 + 2 + (i * 12);
|
|
||||||
unsigned int tag = get_int16(buffer, tag_offset + 0);
|
|
||||||
unsigned int type = get_int16(buffer, tag_offset + 2);
|
|
||||||
unsigned int count = get_int32(buffer, tag_offset + 4);
|
|
||||||
unsigned int offset = get_int32(buffer, tag_offset + 8);
|
|
||||||
|
|
||||||
switch (tag) {
|
|
||||||
case DCPTAG_COLOR_MATRIX_1:
|
|
||||||
for (int j = 0; j < 9; j++) {
|
|
||||||
float point =
|
|
||||||
get_srational(buffer, offset + (j * 8));
|
|
||||||
result.color_matrix_1[j] = point;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DCPTAG_COLOR_MATRIX_2:
|
|
||||||
for (int j = 0; j < 9; j++) {
|
|
||||||
float point =
|
|
||||||
get_srational(buffer, offset + (j * 8));
|
|
||||||
result.color_matrix_2[j] = point;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DCPTAG_FORWARD_MATRIX_1:
|
|
||||||
for (int j = 0; j < 9; j++) {
|
|
||||||
float point =
|
|
||||||
get_srational(buffer, offset + (j * 8));
|
|
||||||
result.forward_matrix_1[j] = point;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DCPTAG_FORWARD_MATRIX_2:
|
|
||||||
for (int j = 0; j < 9; j++) {
|
|
||||||
float point =
|
|
||||||
get_srational(buffer, offset + (j * 8));
|
|
||||||
result.forward_matrix_2[j] = point;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DCPTAG_CALIBRATION_ILLUMINANT_1:
|
|
||||||
result.illuminant_1 = offset;
|
|
||||||
break;
|
|
||||||
case DCPTAG_CALIBRATION_ILLUMINANT_2:
|
|
||||||
result.illuminant_2 = offset;
|
|
||||||
break;
|
|
||||||
case DCPTAG_PROFILE_TONE_CURVE:
|
|
||||||
result.tone_curve = malloc(count * sizeof(float));
|
|
||||||
result.tone_curve_length = count;
|
|
||||||
for (int j = 0; j < count; j++) {
|
|
||||||
result.tone_curve[j] =
|
|
||||||
get_float(buffer, offset + (j * 4));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DCPTAG_PROFILE_HUE_SAT_MAP_DIMS:
|
|
||||||
result.hue_sat_map_dims[0] = get_int32(buffer, offset);
|
|
||||||
result.hue_sat_map_dims[1] = get_int32(buffer, offset + 4);
|
|
||||||
result.hue_sat_map_dims[2] = get_int32(buffer, offset + 8);
|
|
||||||
break;
|
|
||||||
case DCPTAG_PROFILE_HUE_SAT_MAP_DATA_1:
|
|
||||||
result.hue_sat_map_data_1 = malloc(count * sizeof(float));
|
|
||||||
for (int j = 0; j < count; j++) {
|
|
||||||
result.hue_sat_map_data_1[j] =
|
|
||||||
get_float(buffer, offset + (j * 4));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DCPTAG_PROFILE_HUE_SAT_MAP_DATA_2:
|
|
||||||
result.hue_sat_map_data_2 = malloc(count * sizeof(float));
|
|
||||||
for (int j = 0; j < count; j++) {
|
|
||||||
result.hue_sat_map_data_2[j] =
|
|
||||||
get_float(buffer, offset + (j * 4));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
find_calibration_by_model(char *conffile, char *model, const char *sensor)
|
find_calibration_by_model(char *conffile, char *model, const char *sensor)
|
||||||
{
|
{
|
||||||
|
11
src/dcp.h
11
src/dcp.h
@@ -17,17 +17,6 @@ struct MPCameraCalibration {
|
|||||||
float *hue_sat_map_data_2;
|
float *hue_sat_map_data_2;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DCPTAG_COLOR_MATRIX_1 50721
|
|
||||||
#define DCPTAG_COLOR_MATRIX_2 50722
|
|
||||||
#define DCPTAG_PROFILE_HUE_SAT_MAP_DIMS 50937
|
|
||||||
#define DCPTAG_PROFILE_HUE_SAT_MAP_DATA_1 50938
|
|
||||||
#define DCPTAG_PROFILE_HUE_SAT_MAP_DATA_2 50939
|
|
||||||
#define DCPTAG_PROFILE_TONE_CURVE 50940
|
|
||||||
#define DCPTAG_CALIBRATION_ILLUMINANT_1 50778
|
|
||||||
#define DCPTAG_CALIBRATION_ILLUMINANT_2 50779
|
|
||||||
#define DCPTAG_FORWARD_MATRIX_1 50964
|
|
||||||
#define DCPTAG_FORWARD_MATRIX_2 50965
|
|
||||||
|
|
||||||
struct MPCameraCalibration parse_calibration_file(const char *path);
|
struct MPCameraCalibration parse_calibration_file(const char *path);
|
||||||
|
|
||||||
bool find_calibration(char *conffile, const char *sensor);
|
bool find_calibration(char *conffile, const char *sensor);
|
||||||
|
@@ -481,24 +481,51 @@ process_image_for_preview(const uint8_t *image)
|
|||||||
static void
|
static void
|
||||||
process_image_for_capture(const uint8_t *image, int count)
|
process_image_for_capture(const uint8_t *image, int count)
|
||||||
{
|
{
|
||||||
time_t rawtime;
|
|
||||||
time(&rawtime);
|
|
||||||
struct tm tim = *(localtime(&rawtime));
|
|
||||||
|
|
||||||
char datetime[20] = { 0 };
|
|
||||||
strftime(datetime, 20, "%Y:%m:%d %H:%M:%S", &tim);
|
|
||||||
|
|
||||||
char fname[255];
|
char fname[255];
|
||||||
sprintf(fname, "%s/%d.dng", burst_dir, count);
|
sprintf(fname, "%s/%d.dng", burst_dir, count);
|
||||||
|
|
||||||
|
uint16_t orientation;
|
||||||
|
if (state_proc.device_rotation == 0) {
|
||||||
|
orientation = state_proc.mode->mirrored ?
|
||||||
|
LIBDNG_ORIENTATION_TOPRIGHT :
|
||||||
|
LIBDNG_ORIENTATION_TOPLEFT;
|
||||||
|
} else if (state_proc.device_rotation == 90) {
|
||||||
|
orientation = state_proc.mode->mirrored ?
|
||||||
|
LIBDNG_ORIENTATION_RIGHTBOT :
|
||||||
|
LIBDNG_ORIENTATION_LEFTBOT;
|
||||||
|
} else if (state_proc.device_rotation == 180) {
|
||||||
|
orientation = state_proc.mode->mirrored ?
|
||||||
|
LIBDNG_ORIENTATION_BOTLEFT :
|
||||||
|
LIBDNG_ORIENTATION_BOTRIGHT;
|
||||||
|
} else {
|
||||||
|
orientation = state_proc.mode->mirrored ?
|
||||||
|
LIBDNG_ORIENTATION_LEFTTOP :
|
||||||
|
LIBDNG_ORIENTATION_RIGHTTOP;
|
||||||
|
}
|
||||||
|
|
||||||
libdng_info dng = { 0 };
|
libdng_info dng = { 0 };
|
||||||
libdng_new(&dng);
|
libdng_new(&dng);
|
||||||
|
|
||||||
|
libdng_set_datetime_now(&dng);
|
||||||
libdng_set_mode_from_pixfmt(&dng, state_proc.mode->v4l_pixfmt);
|
libdng_set_mode_from_pixfmt(&dng, state_proc.mode->v4l_pixfmt);
|
||||||
libdng_set_make_model(&dng,
|
libdng_set_make_model(&dng,
|
||||||
state_proc.configuration->make,
|
state_proc.configuration->make,
|
||||||
state_proc.configuration->model);
|
state_proc.configuration->model);
|
||||||
|
libdng_set_orientation(&dng, orientation);
|
||||||
|
libdng_set_software(&dng, "Megapixels");
|
||||||
|
libdng_set_neutral(&dng, state_proc.red, 1.0f, state_proc.blue);
|
||||||
|
libdng_set_analog_balance(&dng,
|
||||||
|
state_proc.balance[0],
|
||||||
|
state_proc.balance[1],
|
||||||
|
state_proc.balance[2]);
|
||||||
|
|
||||||
|
if (!state_proc.exposure.manual) {
|
||||||
|
libdng_set_exposure_program(&dng, LIBDNG_EXPOSUREPROGRAM_NORMAL);
|
||||||
|
} else {
|
||||||
|
libdng_set_exposure_program(&dng, LIBDNG_EXPOSUREPROGRAM_MANUAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Writing frame to %s\n", fname);
|
||||||
libdng_write(&dng,
|
libdng_write(&dng,
|
||||||
fname,
|
fname,
|
||||||
state_proc.mode->width,
|
state_proc.mode->width,
|
||||||
@@ -507,84 +534,6 @@ process_image_for_capture(const uint8_t *image, int count)
|
|||||||
count);
|
count);
|
||||||
libdng_free(&dng);
|
libdng_free(&dng);
|
||||||
|
|
||||||
uint16_t orientation;
|
|
||||||
if (state_proc.device_rotation == 0) {
|
|
||||||
orientation = state_proc.mode->mirrored ? ORIENTATION_TOPRIGHT :
|
|
||||||
ORIENTATION_TOPLEFT;
|
|
||||||
} else if (state_proc.device_rotation == 90) {
|
|
||||||
orientation = state_proc.mode->mirrored ? ORIENTATION_RIGHTBOT :
|
|
||||||
ORIENTATION_LEFTBOT;
|
|
||||||
} else if (state_proc.device_rotation == 180) {
|
|
||||||
orientation = state_proc.mode->mirrored ? ORIENTATION_BOTLEFT :
|
|
||||||
ORIENTATION_BOTRIGHT;
|
|
||||||
} else {
|
|
||||||
orientation = state_proc.mode->mirrored ? ORIENTATION_LEFTTOP :
|
|
||||||
ORIENTATION_RIGHTTOP;
|
|
||||||
}
|
|
||||||
TIFFSetField(tif, TIFFTAG_ORIENTATION, orientation);
|
|
||||||
TIFFSetField(tif, TIFFTAG_DATETIME, datetime);
|
|
||||||
TIFFSetField(tif, TIFFTAG_SOFTWARE, "Megapixels");
|
|
||||||
|
|
||||||
static float neutral[] = { 1.0f, 1.0f, 1.0f };
|
|
||||||
neutral[0] = state_proc.red;
|
|
||||||
neutral[2] = state_proc.blue;
|
|
||||||
TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
|
|
||||||
TIFFSetField(tif, TIFFTAG_ANALOGBALANCE, 3, state_proc.balance);
|
|
||||||
|
|
||||||
// Write black thumbnail, only windows uses this
|
|
||||||
{
|
|
||||||
unsigned char *buf = (unsigned char *)calloc(
|
|
||||||
1, (state_proc.mode->width >> 4) * 3);
|
|
||||||
for (int row = 0; row < (state_proc.mode->height >> 4); row++) {
|
|
||||||
TIFFWriteScanline(tif, buf, row, 0);
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
}
|
|
||||||
TIFFWriteDirectory(tif);
|
|
||||||
|
|
||||||
printf("TIFF version %d\n", TIFFLIB_VERSION);
|
|
||||||
int whitelevel =
|
|
||||||
(1 << libmegapixels_format_bits_per_pixel(state_proc.mode->format)) -
|
|
||||||
1;
|
|
||||||
TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &whitelevel);
|
|
||||||
TIFFCheckpointDirectory(tif);
|
|
||||||
printf("Writing frame to %s\n", fname);
|
|
||||||
|
|
||||||
uint8_t *output_image = (uint8_t *)image;
|
|
||||||
|
|
||||||
// Repack 10-bit image from sensor format into a sequencial format
|
|
||||||
if (libmegapixels_format_bits_per_pixel(state_proc.mode->format) == 10) {
|
|
||||||
output_image = malloc(
|
|
||||||
libmegapixels_mode_width_to_bytes(state_proc.mode->format,
|
|
||||||
state_proc.mode->width) *
|
|
||||||
state_proc.mode->height);
|
|
||||||
|
|
||||||
repack_image_sequencial(image, output_image, state_proc.mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int row = 0; row < state_proc.mode->height; row++) {
|
|
||||||
TIFFWriteScanline(tif,
|
|
||||||
(void *)output_image +
|
|
||||||
(row * libmegapixels_mode_width_to_bytes(
|
|
||||||
state_proc.mode->format,
|
|
||||||
state_proc.mode->width)),
|
|
||||||
row,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
TIFFWriteDirectory(tif);
|
|
||||||
|
|
||||||
if (output_image != image)
|
|
||||||
free(output_image);
|
|
||||||
|
|
||||||
// Add an EXIF block to the tiff
|
|
||||||
TIFFCreateEXIFDirectory(tif);
|
|
||||||
// 1 = manual, 2 = full auto, 3 = aperture priority, 4 = shutter priority
|
|
||||||
if (!state_proc.exposure.manual) {
|
|
||||||
TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 2);
|
|
||||||
} else {
|
|
||||||
TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TIFFSetField(tif,
|
TIFFSetField(tif,
|
||||||
EXIFTAG_EXPOSURETIME,
|
EXIFTAG_EXPOSURETIME,
|
||||||
@@ -610,8 +559,6 @@ process_image_for_capture(const uint8_t *image, int count)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime);
|
|
||||||
TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime);
|
|
||||||
/*
|
/*
|
||||||
if (pr_camera->fnumber) {
|
if (pr_camera->fnumber) {
|
||||||
TIFFSetField(tif, EXIFTAG_FNUMBER, pr_camera->fnumber);
|
TIFFSetField(tif, EXIFTAG_FNUMBER, pr_camera->fnumber);
|
||||||
@@ -626,16 +573,6 @@ process_image_for_capture(const uint8_t *image, int count)
|
|||||||
pr_camera->cropfactor));
|
pr_camera->cropfactor));
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
uint64_t exif_offset = 0;
|
|
||||||
TIFFWriteCustomDirectory(tif, &exif_offset);
|
|
||||||
TIFFFreeDirectory(tif);
|
|
||||||
|
|
||||||
// Update exif pointer
|
|
||||||
TIFFSetDirectory(tif, 0);
|
|
||||||
TIFFSetField(tif, TIFFTAG_EXIFIFD, exif_offset);
|
|
||||||
TIFFRewriteDirectory(tif);
|
|
||||||
|
|
||||||
TIFFClose(tif);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Reference in New Issue
Block a user