diff --git a/data/debayer.frag b/data/debayer.frag index 48200b8..dd65a65 100644 --- a/data/debayer.frag +++ b/data/debayer.frag @@ -4,6 +4,7 @@ precision highp float; uniform sampler2D texture; uniform mat3 color_matrix; +uniform float inv_gamma; #ifdef BITS_10 uniform float row_length; uniform float padding_ratio; @@ -62,13 +63,13 @@ main() // should be an uniform vec3 corrected = color - 0.02; -#if defined(COLORSPACE_RAW) + /* // Fast SRGB estimate. See https://mimosa-pudica.net/fast-gamma/ - vec3 srgb_color = + vec3 gamma_color = (vec3(1.138) * inversesqrt(corrected) - vec3(0.138)) * corrected; + */ + color = color * color_matrix; - gl_FragColor = vec4(srgb_color, 1); -#elif defined(COLORSPACE_SRGB) - gl_FragColor = vec4(color, 1); -#endif + vec3 gamma_color = pow(color, vec3(inv_gamma)); + gl_FragColor = vec4(gamma_color, 1); } diff --git a/src/dcp.c b/src/dcp.c index 98abbb6..2acaa3b 100644 --- a/src/dcp.c +++ b/src/dcp.c @@ -1,8 +1,10 @@ #include "dcp.h" +#include #include #include #include +#include unsigned int get_int32(const unsigned char *buffer, size_t offset) @@ -147,3 +149,63 @@ parse_calibration_file(const char *path) return result; } + +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 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; +} + +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; + } + } +} diff --git a/src/dcp.h b/src/dcp.h index 6b1eb50..ca8902f 100644 --- a/src/dcp.h +++ b/src/dcp.h @@ -1,5 +1,6 @@ #pragma once +#include #include struct MPCameraCalibration { @@ -28,3 +29,5 @@ struct MPCameraCalibration { #define DCPTAG_FORWARD_MATRIX_2 50965 struct MPCameraCalibration parse_calibration_file(const char *path); + +bool find_calibration(char *conffile, const char *sensor); diff --git a/src/gles2_debayer.c b/src/gles2_debayer.c index 7eb23f7..e02bfe5 100644 --- a/src/gles2_debayer.c +++ b/src/gles2_debayer.c @@ -1,6 +1,7 @@ #include "gles2_debayer.h" #include "camera.h" +#include "dcp.h" #include "gl_util.h" #include @@ -18,12 +19,13 @@ struct _GLES2Debayer { GLuint uniform_texture; GLuint uniform_color_matrix; GLuint uniform_row_length; + GLuint uniform_inv_gamma; GLuint quad; }; GLES2Debayer * -gles2_debayer_new(int format, int xfer) +gles2_debayer_new(int format) { uint32_t pixfmt = libmegapixels_format_to_v4l_pixfmt(format); if (pixfmt != V4L2_PIX_FMT_SBGGR8 && pixfmt != V4L2_PIX_FMT_SGBRG8 && @@ -37,14 +39,12 @@ gles2_debayer_new(int format, int xfer) glGenFramebuffers(1, &frame_buffer); check_gl(); - char format_def[96]; - bool is_srgb = xfer == LIBMEGAPIXELS_XFER_SRGB; + char format_def[64]; snprintf(format_def, - 96, - "#define CFA_%s\n#define BITS_%d\n#define COLORSPACE_%s\n", + 64, + "#define CFA_%s\n#define BITS_%d\n", libmegapixels_format_cfa(format), - libmegapixels_format_bits_per_pixel(format), - is_srgb ? "SRGB" : "RAW"); + libmegapixels_format_bits_per_pixel(format)); const GLchar *def[1] = { format_def }; @@ -77,6 +77,7 @@ gles2_debayer_new(int format, int xfer) self->uniform_texture = glGetUniformLocation(self->program, "texture"); self->uniform_color_matrix = glGetUniformLocation(self->program, "color_matrix"); + self->uniform_inv_gamma = glGetUniformLocation(self->program, "inv_gamma"); if (libmegapixels_format_bits_per_pixel(self->format) == 10) self->uniform_row_length = glGetUniformLocation(self->program, "row_length"); @@ -115,8 +116,7 @@ gles2_debayer_configure(GLES2Debayer *self, const uint32_t src_height, const uint32_t rotation, const bool mirrored, - const float *colormatrix, - const uint8_t blacklevel) + struct MPCameraCalibration calibration) { glViewport(0, 0, (int)dst_width, (int)dst_height); check_gl(); @@ -142,11 +142,21 @@ gles2_debayer_configure(GLES2Debayer *self, glUniform2f(self->uniform_pixel_size, pixel_size_x, pixel_size_y); check_gl(); - if (colormatrix) { + float gamma = 1.0f; + for (int i = 0; i < calibration.tone_curve_length * 2; i += 2) { + float g = calibration.tone_curve[i + 1] / calibration.tone_curve[i]; + if (g > gamma) { + gamma = g; + } + } + glUniform1f(self->uniform_inv_gamma, 1.0f / gamma); + + if (calibration.color_matrix_1[0]) { GLfloat transposed[9]; for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) - transposed[i + j * 3] = colormatrix[j + i * 3]; + transposed[i + j * 3] = + calibration.color_matrix_1[j + i * 3]; glUniformMatrix3fv( self->uniform_color_matrix, 1, GL_FALSE, transposed); diff --git a/src/gles2_debayer.h b/src/gles2_debayer.h index f719a0f..e9e18de 100644 --- a/src/gles2_debayer.h +++ b/src/gles2_debayer.h @@ -1,11 +1,13 @@ #include "camera.h" +#include "dcp.h" #include "gl_util.h" #include #include typedef struct _GLES2Debayer GLES2Debayer; -GLES2Debayer *gles2_debayer_new(int format, int xfer); +GLES2Debayer *gles2_debayer_new(int format); + void gles2_debayer_free(GLES2Debayer *self); void gles2_debayer_use(GLES2Debayer *self); @@ -17,7 +19,6 @@ void gles2_debayer_configure(GLES2Debayer *self, const uint32_t src_height, const uint32_t rotation, const bool mirrored, - const float *colormatrix, - const uint8_t blacklevel); + struct MPCameraCalibration calibration); void gles2_debayer_process(GLES2Debayer *self, GLuint dst_id, GLuint source_id); diff --git a/src/process_pipeline.c b/src/process_pipeline.c index 68f20fb..455e538 100644 --- a/src/process_pipeline.c +++ b/src/process_pipeline.c @@ -927,7 +927,7 @@ on_output_changed(bool format_changed) if (gles2_debayer) gles2_debayer_free(gles2_debayer); - gles2_debayer = gles2_debayer_new(state_proc.mode->format, state_proc.mode->xfer); + gles2_debayer = gles2_debayer_new(state_proc.mode->format); check_gl(); gles2_debayer_use(gles2_debayer); @@ -940,8 +940,7 @@ on_output_changed(bool format_changed) state_proc.mode->height, state_proc.mode->rotation, 0, - NULL, - 0); + state_proc.calibration); } static int @@ -954,6 +953,7 @@ mod(int a, int b) static void update_state(MPPipeline *pipeline, const mp_state_proc *new_state) { + bool camera_changed = state_proc.camera != new_state->camera; state_proc.configuration = new_state->configuration; state_proc.camera = new_state->camera; @@ -1008,6 +1008,15 @@ update_state(MPPipeline *pipeline, const mp_state_proc *new_state) on_output_changed(format_changed); } + if (camera_changed) { + char *cf; + if (find_calibration(cf, state_proc.camera->name)) { + state_proc.calibration = parse_calibration_file(cf); + } else { + printf("No calibration for %s\n", state_proc.camera->name); + } + } + mp_state_main new_main = { .camera = pr_camera, .has_auto_focus_continuous = false, diff --git a/src/state.h b/src/state.h index a3452b1..e1d6047 100644 --- a/src/state.h +++ b/src/state.h @@ -1,4 +1,5 @@ #pragma once +#include "dcp.h" #include #include @@ -91,4 +92,6 @@ typedef struct state_proc { controlstate gain; controlstate exposure; controlstate focus; + + struct MPCameraCalibration calibration; } mp_state_proc; \ No newline at end of file