Add DCP calibration files for sensors

Read .dcp files stored in the same directory as the camera configuration
files and copy over the calibration curves into the DNG files after
taking a picture.
This commit is contained in:
Martijn Braam
2023-03-07 18:42:48 +01:00
parent 92f5270f87
commit b5161db18e
10 changed files with 397 additions and 3 deletions

BIN
config/pine64,pinephone,front.dcp Executable file

Binary file not shown.

BIN
config/pine64,pinephone,rear.dcp Executable file

Binary file not shown.

View File

@@ -61,6 +61,7 @@ executable('megapixels',
'src/pipeline.c',
'src/process_pipeline.c',
'src/zbar_pipeline.c',
'src/dcp.c',
resources,
include_directories: 'src/',
dependencies: [gtkdep, libfeedback, libm, tiff, zbar, threads, epoxy] + optdeps,
@@ -70,6 +71,8 @@ executable('megapixels',
install_data(
[
'config/pine64,pinephone-1.0.ini',
'config/pine64,pinephone,rear.dcp',
'config/pine64,pinephone,front.dcp',
'config/pine64,pinephone-1.1.ini',
'config/pine64,pinephone-1.2.ini',
'config/pine64,pinetab.ini',

View File

@@ -73,6 +73,77 @@ find_config(char *conffile)
return false;
}
static bool
find_calibration_by_model(char *conffile, char *model, const char *sensor)
{
// Check config/%model,%sensor.dcp in the current working directory
sprintf(conffile, "config/%s,%s.dcp", model, sensor);
if (access(conffile, F_OK) != -1) {
printf("Found calibration file at %s\n", conffile);
return true;
}
// Check for a calibration file in XDG_CONFIG_HOME
sprintf(conffile,
"%s/megapixels/config/%s,%s.dcp",
g_get_user_config_dir(),
model,
sensor);
if (access(conffile, F_OK) != -1) {
printf("Found calibration file at %s\n", conffile);
return true;
}
// Check user overridden /etc/megapixels/config/%model,%sensor.dcp
sprintf(conffile,
"%s/megapixels/config/%s,%s.dcp",
SYSCONFDIR,
model,
sensor);
if (access(conffile, F_OK) != -1) {
printf("Found calibration file at %s\n", conffile);
return true;
}
// Check packaged /usr/share/megapixels/config/%model,%sensor.ini
sprintf(conffile, "%s/megapixels/config/%s,%s.dcp", DATADIR, model, sensor);
if (access(conffile, F_OK) != -1) {
printf("Found calibration file at %s\n", conffile);
return true;
}
printf("No calibration found for %s,%s\n", model, sensor);
return false;
}
static bool
find_calibration(char *conffile, const char *sensor)
{
char model[512];
FILE *fp;
if (access("/proc/device-tree/compatible", F_OK) == -1) {
return false;
}
fp = fopen("/proc/device-tree/compatible", "r");
char *modelptr = model;
while (1) {
int c = fgetc(fp);
if (c == EOF) {
*(modelptr) = '\0';
return find_calibration_by_model(conffile, model, sensor);
}
*(modelptr++) = (char)c;
if (c == 0) {
bool res =
find_calibration_by_model(conffile, model, sensor);
if (res) {
return true;
}
modelptr = model;
}
}
}
static int
strtoint(const char *nptr, char **endptr, int base)
{
@@ -146,6 +217,12 @@ config_ini_handler(void *user,
cameras[index].index = index;
strcpy(cameras[index].cfg_name, section);
char cal_path[512];
if (find_calibration(cal_path, section)) {
cameras[index].calibration =
parse_calibration_file(cal_path);
}
}
struct mp_camera_config *cc = &cameras[index];

View File

@@ -1,5 +1,6 @@
#pragma once
#include "dcp.h"
#include "mode.h"
#include <stdbool.h>
@@ -36,6 +37,8 @@ struct mp_camera_config {
int blacklevel;
int whitelevel;
struct MPCameraCalibration calibration;
float focallength;
float cropfactor;
double fnumber;

149
src/dcp.c Normal file
View File

@@ -0,0 +1,149 @@
#include "dcp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.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;
}

30
src/dcp.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#include <stdio.h>
struct MPCameraCalibration {
float color_matrix_1[9];
float color_matrix_2[9];
float forward_matrix_1[9];
float forward_matrix_2[9];
unsigned short illuminant_1;
unsigned short illuminant_2;
unsigned int hue_sat_map_dims[3];
size_t tone_curve_length;
float *tone_curve;
float *hue_sat_map_data_1;
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);

View File

@@ -375,6 +375,18 @@ update_process_pipeline()
mp_camera_control_get_int32(info->camera, V4L2_CID_EXPOSURE);
}
MPControl control;
float balance_red = 1.0f;
float balance_blue = 1.0f;
if (mp_camera_query_control(info->camera, V4L2_CID_RED_BALANCE, &control)) {
int red = mp_camera_control_get_int32(info->camera,
V4L2_CID_RED_BALANCE);
int blue = mp_camera_control_get_int32(info->camera,
V4L2_CID_BLUE_BALANCE);
balance_red = (float)red / (float)control.max;
balance_blue = (float)blue / (float)control.max;
}
struct mp_process_pipeline_state pipeline_state = {
.camera = camera,
.mode = mode,
@@ -385,6 +397,8 @@ update_process_pipeline()
.gain_is_manual = current_controls.gain_is_manual,
.gain = current_controls.gain,
.gain_max = info->gain_max,
.balance_red = balance_red,
.balance_blue = balance_blue,
.exposure_is_manual = current_controls.exposure_is_manual,
.exposure = current_controls.exposure,
.has_auto_focus_continuous = info->has_auto_focus_continuous,

View File

@@ -15,6 +15,7 @@
#include <sys/mman.h>
#define TIFFTAG_FORWARDMATRIX1 50964
#define TIFFTAG_FORWARDMATRIX2 50965
static const float colormatrix_srgb[] = { 3.2409, -1.5373, -0.4986, -0.9692, 1.8759,
0.0415, 0.0556, -0.2039, 1.0569 };
@@ -46,6 +47,7 @@ static int output_buffer_height = -1;
// static bool gain_is_manual;
static int gain;
static int gain_max;
static float balance[3] = { 1.0f, 1.0f, 1.0f };
static bool exposure_is_manual;
static int exposure;
@@ -68,6 +70,46 @@ register_custom_tiff_tags(TIFF *tif)
1,
1,
"ForwardMatrix1" },
{ TIFFTAG_FORWARDMATRIX2,
-1,
-1,
TIFF_SRATIONAL,
FIELD_CUSTOM,
1,
1,
"ForwardMatrix2" },
{ DCPTAG_PROFILE_TONE_CURVE,
-1,
-1,
TIFF_FLOAT,
FIELD_CUSTOM,
1,
1,
"ProfileToneCurve" },
{ DCPTAG_PROFILE_HUE_SAT_MAP_DIMS,
-1,
-1,
TIFF_FLOAT,
FIELD_CUSTOM,
1,
1,
"ProfileHueSatMapDims" },
{ DCPTAG_PROFILE_HUE_SAT_MAP_DATA_1,
-1,
-1,
TIFF_FLOAT,
FIELD_CUSTOM,
1,
1,
"ProfileHueSatMapData1" },
{ DCPTAG_PROFILE_HUE_SAT_MAP_DATA_2,
-1,
-1,
TIFF_FLOAT,
FIELD_CUSTOM,
1,
1,
"ProfileHueSatMapData2" },
};
// Add missing dng fields
@@ -529,17 +571,88 @@ process_image_for_capture(const uint8_t *image, int count)
mp_get_device_make(),
mp_get_device_model());
TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, uniquecameramodel);
if (camera->colormatrix[0]) {
// Color matrices
if (camera->calibration.color_matrix_1[0]) {
TIFFSetField(tif,
TIFFTAG_COLORMATRIX1,
9,
camera->calibration.color_matrix_1);
} else if (camera->colormatrix[0]) {
TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, camera->colormatrix);
} else {
TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colormatrix_srgb);
}
if (camera->forwardmatrix[0]) {
if (camera->calibration.color_matrix_2[0]) {
TIFFSetField(tif,
TIFFTAG_COLORMATRIX2,
9,
camera->calibration.color_matrix_2);
}
if (camera->calibration.forward_matrix_1[0]) {
TIFFSetField(tif,
TIFFTAG_FORWARDMATRIX1,
9,
camera->calibration.forward_matrix_1);
} else if (camera->forwardmatrix[0]) {
TIFFSetField(tif, TIFFTAG_FORWARDMATRIX1, 9, camera->forwardmatrix);
}
if (camera->calibration.forward_matrix_2[0]) {
TIFFSetField(tif,
TIFFTAG_FORWARDMATRIX2,
9,
camera->calibration.forward_matrix_2);
}
static const float neutral[] = { 1.0, 1.0, 1.0 };
TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
if (camera->calibration.illuminant_1) {
TIFFSetField(tif,
TIFFTAG_CALIBRATIONILLUMINANT1,
camera->calibration.illuminant_1);
} else {
TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
}
if (camera->calibration.illuminant_2) {
TIFFSetField(tif,
TIFFTAG_CALIBRATIONILLUMINANT2,
camera->calibration.illuminant_2);
}
if (camera->calibration.tone_curve_length) {
TIFFSetField(tif,
DCPTAG_PROFILE_TONE_CURVE,
camera->calibration.tone_curve_length,
camera->calibration.tone_curve);
}
if (camera->calibration.hue_sat_map_dims[0]) {
TIFFSetField(tif,
DCPTAG_PROFILE_HUE_SAT_MAP_DIMS,
3,
camera->calibration.hue_sat_map_dims);
TIFFSetField(tif,
DCPTAG_PROFILE_HUE_SAT_MAP_DATA_1,
camera->calibration.hue_sat_map_dims[0] *
camera->calibration.hue_sat_map_dims[1] *
camera->calibration.hue_sat_map_dims[2] * 3,
camera->calibration.hue_sat_map_data_1);
if (camera->calibration.hue_sat_map_data_2 != NULL) {
TIFFSetField(
tif,
DCPTAG_PROFILE_HUE_SAT_MAP_DATA_2,
camera->calibration.hue_sat_map_dims[0] *
camera->calibration.hue_sat_map_dims[1] *
camera->calibration.hue_sat_map_dims[2] * 3,
camera->calibration.hue_sat_map_data_2);
}
}
TIFFSetField(tif, TIFFTAG_ANALOGBALANCE, 3, balance);
// Write black thumbnail, only windows uses this
{
unsigned char *buf =
@@ -932,6 +1045,8 @@ update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state
// gain_is_manual = state->gain_is_manual;
gain = state->gain;
gain_max = state->gain_max;
balance[0] = state->balance_red;
balance[2] = state->balance_blue;
exposure_is_manual = state->exposure_is_manual;
exposure = state->exposure;

View File

@@ -21,6 +21,9 @@ struct mp_process_pipeline_state {
int gain;
int gain_max;
float balance_red;
float balance_blue;
bool exposure_is_manual;
int exposure;