[gnome-color-manager] huey: add a test program 'gcm-calculate-fudge' to find calibration parameters
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-color-manager] huey: add a test program 'gcm-calculate-fudge' to find calibration parameters
- Date: Fri, 24 Sep 2010 22:49:06 +0000 (UTC)
commit f217586fba4ef027c3b331095732a1a04a183462
Author: Richard Hughes <richard hughsie com>
Date: Fri Sep 24 23:38:51 2010 +0100
huey: add a test program 'gcm-calculate-fudge' to find calibration parameters
The Vec3 found in the EEPROM is very similar to the dark offset, which I
always theorised was required. Just subtracting this from the raw RGB value
gives the wrong answer, and removing it after the calibration matrix is
even more wrong.
Using the values from argyll -v9 we can get the raw USB values. These can
be decoded (we know how) and compared against the argyll XYZ values.
By using two nested loops we can find the optimal value of pre and post
multipliers. This gives us the ideal pre-ratio of 1982, which seems
pretty random. 2000 makes more sense.
Using the new pre-and-post multipliers, and the dark calibration vector,
we can get within 0.8% of the argyll values. That's close enough for me,
it's late here and I'm tired.
docs/huey/.gitignore | 1 +
docs/huey/Makefile.am | 17 +++-
docs/huey/gcm-calculate-fudge.c | 195 +++++++++++++++++++++++++++++++++++++++
libcolor-glib/gcm-sensor-huey.c | 78 ++++++++++------
4 files changed, 261 insertions(+), 30 deletions(-)
---
diff --git a/docs/huey/.gitignore b/docs/huey/.gitignore
index afa4bb6..91487bd 100644
--- a/docs/huey/.gitignore
+++ b/docs/huey/.gitignore
@@ -2,5 +2,6 @@
.libs
gcm-dump-to-values
gcm-parse-huey
+gcm-calculate-fudge
*.o
*.parsed
diff --git a/docs/huey/Makefile.am b/docs/huey/Makefile.am
index 2bc378e..f3d3c81 100644
--- a/docs/huey/Makefile.am
+++ b/docs/huey/Makefile.am
@@ -5,10 +5,24 @@ INCLUDES = \
$(GTK_CFLAGS) \
$(GLIB_CFLAGS)
+COLOR_GLIB_LIBS = \
+ $(top_builddir)/libcolor-glib/libcolor-glib.la
+
noinst_PROGRAMS = \
gcm-parse-huey \
+ gcm-calculate-fudge \
gcm-dump-to-values
+gcm_calculate_fudge_SOURCES = \
+ gcm-calculate-fudge.c
+
+gcm_calculate_fudge_LDADD = \
+ $(COLOR_GLIB_LIBS) \
+ $(GLIB_LIBS)
+
+gcm_calculate_fudge_CFLAGS = \
+ $(WARNINGFLAGS_C)
+
gcm_parse_huey_SOURCES = \
gcm-parse-huey.c
@@ -21,9 +35,6 @@ gcm_parse_huey_CFLAGS = \
gcm_dump_to_values_SOURCES = \
gcm-dump-to-values.c
-COLOR_GLIB_LIBS = \
- $(top_builddir)/libcolor-glib/libcolor-glib.la
-
gcm_dump_to_values_LDADD = \
$(COLOR_GLIB_LIBS) \
$(GLIB_LIBS)
diff --git a/docs/huey/gcm-calculate-fudge.c b/docs/huey/gcm-calculate-fudge.c
new file mode 100644
index 0000000..4bd0f59
--- /dev/null
+++ b/docs/huey/gcm-calculate-fudge.c
@@ -0,0 +1,195 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <glib.h>
+#include <libcolor-glib.h>
+
+typedef struct {
+ guint16 R;
+ guint16 G;
+ guint16 B;
+} GcmSensorHueyMultiplier;
+
+
+#if 0
+{
+ guchar threshold_buffer[16];
+ guchar result_buffer[16];
+ GcmSensorHueyMultiplier mult;
+ threshold_buffer[0] = 0x02;
+ threshold_buffer[1] = 0x8b;
+ threshold_buffer[2] = 0x03;
+ threshold_buffer[3] = 0x64;
+ threshold_buffer[4] = 0x01;
+ threshold_buffer[5] = 0x9b;
+
+ mult.R = gcm_buffer_read_uint16_be (threshold_buffer+0);
+ mult.G = gcm_buffer_read_uint16_be (threshold_buffer+2);
+ mult.B = gcm_buffer_read_uint16_be (threshold_buffer+4);
+
+ g_debug ("multiplier = %i, %i, %i", mult.R, mult.G, mult.B);
+
+ result_buffer[0] = 0x10;
+ result_buffer[1] = 0x42;
+ result_buffer[2] = 0x0f;
+ result_buffer[3] = 0x5b;
+ result_buffer[4] = 0x0f;
+ result_buffer[5] = 0x49;
+
+ device_rgb[0].R = (gdouble) mult.R / (gdouble)gcm_buffer_read_uint16_be (result_buffer+0);
+ device_rgb[0].G = (gdouble) mult.G / (gdouble)gcm_buffer_read_uint16_be (result_buffer+2);
+ device_rgb[0].B = (gdouble) mult.B / (gdouble)gcm_buffer_read_uint16_be (result_buffer+4);
+
+ /* get colors as vectors */
+ color_native_vec3 = gcm_color_get_RGB_Vec3 (&device_rgb[0]);
+ color_result_vec3 = gcm_color_get_XYZ_Vec3 (&gcm_xyz);
+
+ /* the matrix of data is designed to convert from 'device RGB' to XYZ */
+ gcm_mat33_vector_multiply (&calibration, color_native_vec3, color_result_vec3);
+
+ /* scale correct */
+ gcm_vec3_scalar_multiply (color_result_vec3, HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR, color_result_vec3);
+}
+#endif
+
+static gdouble
+get_error (const GcmColorXYZ *actual, const GcmColorXYZ *measured)
+{
+ return fabs ((actual->X - measured->X) / measured->X) +
+ fabs ((actual->Y - measured->Y) / measured->Y) +
+ fabs ((actual->Z - measured->Z) / measured->Z);
+}
+
+/**
+ * gcm_sensor_huey_convert_device_RGB_to_XYZ:
+ *
+ * / X \ (( / R \ ) / d \ / c a l \ )
+ * | Y | = (( | G | x pre-scale ) - | r | * | m a t | ) x post_scale
+ * \ Z / (( \ B / ) \ k / \ l c d / )
+ *
+ * The device RGB values have to be scaled to something in the same
+ * scale as the dark calibration. The results then have to be scaled
+ * after convolving. I assume the first is a standard value, and the
+ * second scale must be available in the eeprom somewhere.
+ **/
+static void
+gcm_sensor_huey_convert_device_RGB_to_XYZ (GcmColorRGB *src, GcmColorXYZ *dest,
+ GcmMat3x3 *calibration, GcmVec3 *dark_offset,
+ gdouble pre_scale, gdouble post_scale)
+{
+ GcmVec3 *color_native_vec3;
+ GcmVec3 *color_result_vec3;
+ GcmVec3 temp;
+
+ /* pre-multiply */
+ color_native_vec3 = gcm_color_get_RGB_Vec3 (src);
+ gcm_vec3_scalar_multiply (color_native_vec3, pre_scale, &temp);
+
+ /* remove dark calibration */
+ gcm_vec3_subtract (&temp, dark_offset, &temp);
+
+ /* convolve */
+ color_result_vec3 = gcm_color_get_XYZ_Vec3 (dest);
+ gcm_mat33_vector_multiply (calibration, &temp, color_result_vec3);
+
+ /* post-multiply */
+ gcm_vec3_scalar_multiply (color_result_vec3, post_scale, color_result_vec3);
+}
+
+gint
+main (gint argc, gchar *argv[])
+{
+ GcmColorXYZ actual_xyz[5];
+ GcmColorRGB device_rgb[5];
+ GcmColorXYZ gcm_xyz;
+ GcmMat3x3 calibration;
+ GcmVec3 dark_offset;
+ gdouble *data;
+ gdouble error;
+ gdouble pre_scalar;
+ gdouble post_scalar;
+ gdouble min_error;
+ gdouble best_post_scalar;
+ gdouble best_pre_scalar;
+ guint i;
+
+ /* get the device RGB measured values */
+ gcm_color_init_RGB (&device_rgb[0], 0.082935, 0.053567, 0.001294);
+ gcm_color_init_RGB (&device_rgb[1], 0.066773, 0.150323, 0.009683);
+ gcm_color_init_RGB (&device_rgb[2], 0.013250, 0.021211, 0.095019);
+ gcm_color_init_RGB (&device_rgb[3], 0.156415, 0.220809, 0.105035);
+ gcm_color_init_RGB (&device_rgb[4], 0.000310, 0.000513, 0.000507);
+
+ /* get some results from argyll */
+ gcm_color_init_XYZ (&actual_xyz[0], 82.537676, 42.634870, 2.142396);
+ gcm_color_init_XYZ (&actual_xyz[1], 61.758330, 122.072291, 17.345163);
+ gcm_color_init_XYZ (&actual_xyz[2], 36.544046, 19.224371, 161.438049);
+ gcm_color_init_XYZ (&actual_xyz[3], 174.129280, 180.500098, 179.302163);
+ gcm_color_init_XYZ (&actual_xyz[4], 0.407554, 0.419799, 0.849899);
+
+ /* get the calibration vector */
+ gcm_vec3_init (&dark_offset, 0.014000, 0.014000, 0.016226);
+
+ /* get the calibration matrix */
+ data = gcm_mat33_get_data (&calibration);
+ data[0] = 0.154293;
+ data[1] = -0.009611;
+ data[2] = 0.038087;
+ data[3] = -0.002070;
+ data[4] = 0.122019;
+ data[5] = 0.003279;
+ data[6] = -0.000930;
+ data[7] = 0.001326;
+ data[8] = 0.253616;
+
+ best_post_scalar = 0.0f;
+ best_pre_scalar = 0.0f;
+ min_error = 999999.0f;
+ for (pre_scalar = 1900.0f; pre_scalar < 2100.0f; pre_scalar+=1.0f) {
+ for (post_scalar = 0.25f; post_scalar < 5.0f; post_scalar += 0.000125f) {
+ error = 0.0f;
+ for (i=0; i<5; i++) {
+ gcm_sensor_huey_convert_device_RGB_to_XYZ (&device_rgb[i],
+ &gcm_xyz,
+ &calibration,
+ &dark_offset,
+ pre_scalar,
+ post_scalar);
+// g_debug ("gcolor-XYZ = %f,\t%f,\t%f", gcm_xyz.X, gcm_xyz.Y, gcm_xyz.Z);
+// g_debug ("argyll-XYZ = %f,\t%f,\t%f", actual_xyz[i].X, actual_xyz[i].Y, actual_xyz[i].Z);
+ error += get_error (&actual_xyz[i], &gcm_xyz);
+ }
+ if (error < min_error) {
+ min_error = error;
+ best_post_scalar = post_scalar;
+ best_pre_scalar = pre_scalar;
+ }
+ }
+ }
+ g_debug ("best error=%lf%% @ pre %lf, post %lf", min_error * 100.0f, best_pre_scalar, best_post_scalar);
+
+ return 0;
+}
diff --git a/libcolor-glib/gcm-sensor-huey.c b/libcolor-glib/gcm-sensor-huey.c
index 1728f3f..9e767a6 100644
--- a/libcolor-glib/gcm-sensor-huey.c
+++ b/libcolor-glib/gcm-sensor-huey.c
@@ -55,7 +55,7 @@ struct _GcmSensorHueyPrivate
GcmMat3x3 calibration_lcd;
GcmMat3x3 calibration_crt;
gfloat calibration_value;
- GcmVec3 calibration_vector;
+ GcmVec3 dark_offset;
gchar unlock_string[5];
};
@@ -329,7 +329,7 @@ G_DEFINE_TYPE (GcmSensorHuey, gcm_sensor_huey, GCM_TYPE_SENSOR)
/* Picked out of thin air, just to try to match reality...
* I have no idea why we need to do this, although it probably
* indicates we doing something wrong. */
-#define HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR 6880.0f
+#define HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR 3.347250f
/*
* Register map:
@@ -350,7 +350,7 @@ G_DEFINE_TYPE (GcmSensorHuey, gcm_sensor_huey, GCM_TYPE_SENSOR)
#define HUEY_EEPROM_ADDR_CALIBRATION_TIME_LCD 0x32 /* 4 bytes */
#define HUEY_EEPROM_ADDR_CALIBRATION_DATA_CRT 0x36 /* 36 bytes */
#define HUEY_EEPROM_ADDR_CALIBRATION_TIME_CRT 0x5a /* 4 bytes */
-#define HUEY_EEPROM_ADDR_CALIB_VECTOR 0x67 /* 12 bytes */
+#define HUEY_EEPROM_ADDR_DARK_OFFSET 0x67 /* 12 bytes */
#define HUEY_EEPROM_ADDR_UNLOCK 0x7a /* 5 bytes */
#define HUEY_EEPROM_ADDR_CALIB_VALUE 0x94 /* 4 bytes */
@@ -839,10 +839,41 @@ out:
return ret;
}
-/* in a dark box, the sensors still report a reading */
-#define HUEY_ABSOLUTE_OFFSET_RED 0.000119
-#define HUEY_ABSOLUTE_OFFSET_GREEN 0.000119
-#define HUEY_ABSOLUTE_OFFSET_BLUE 0.000018
+/**
+ * gcm_sensor_huey_convert_device_RGB_to_XYZ:
+ *
+ * / X \ (( / R \ ) / d \ / c a l \ )
+ * | Y | = (( | G | x pre-scale ) - | r | * | m a t | ) x post_scale
+ * \ Z / (( \ B / ) \ k / \ l c d / )
+ *
+ * The device RGB values have to be scaled to something in the same
+ * scale as the dark calibration. The results then have to be scaled
+ * after convolving. I assume the first is a standard value, and the
+ * second scale must be available in the eeprom somewhere.
+ **/
+static void
+gcm_sensor_huey_convert_device_RGB_to_XYZ (GcmColorRGB *src, GcmColorXYZ *dest,
+ GcmMat3x3 *calibration, GcmVec3 *dark_offset,
+ gdouble pre_scale, gdouble post_scale)
+{
+ GcmVec3 *color_native_vec3;
+ GcmVec3 *color_result_vec3;
+ GcmVec3 temp;
+
+ /* pre-multiply */
+ color_native_vec3 = gcm_color_get_RGB_Vec3 (src);
+ gcm_vec3_scalar_multiply (color_native_vec3, pre_scale, &temp);
+
+ /* remove dark calibration */
+ gcm_vec3_subtract (&temp, dark_offset, &temp);
+
+ /* convolve */
+ color_result_vec3 = gcm_color_get_XYZ_Vec3 (dest);
+ gcm_mat33_vector_multiply (calibration, &temp, color_result_vec3);
+
+ /* post-multiply */
+ gcm_vec3_scalar_multiply (color_result_vec3, post_scale, color_result_vec3);
+}
static void
gcm_sensor_huey_sample_thread_cb (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable)
@@ -856,8 +887,6 @@ gcm_sensor_huey_sample_thread_cb (GSimpleAsyncResult *res, GObject *object, GCan
GcmColorXYZ *tmp;
GcmSensorHueyMultiplier multiplier;
GcmSensorHuey *sensor_huey = GCM_SENSOR_HUEY (sensor);
- GcmVec3 *color_native_vec3;
- GcmVec3 *color_result_vec3;
GcmMat3x3 *device_calibration;
GcmSensorOutputType output_type;
@@ -918,12 +947,7 @@ gcm_sensor_huey_sample_thread_cb (GSimpleAsyncResult *res, GObject *object, GCan
goto out;
}
- /* get colors as vectors */
- color_native_vec3 = gcm_color_get_RGB_Vec3 (&color_native);
- color_result_vec3 = gcm_color_get_XYZ_Vec3 (&color_result);
-
egg_debug ("scaled values: red=%0.6lf, green=%0.6lf, blue=%0.6lf", color_native.R, color_native.G, color_native.B);
- egg_debug ("PRE MULTIPLY: %s\n", gcm_vec3_to_string (color_native_vec3));
/* we use different calibration matrices for each output type */
if (output_type == GCM_SENSOR_OUTPUT_TYPE_LCD) {
@@ -934,13 +958,13 @@ gcm_sensor_huey_sample_thread_cb (GSimpleAsyncResult *res, GObject *object, GCan
device_calibration = &sensor_huey->priv->calibration_crt;
}
- /* the matrix of data is designed to convert from 'device RGB' to XYZ */
- gcm_mat33_vector_multiply (device_calibration, color_native_vec3, color_result_vec3);
-
- /* scale correct */
- gcm_vec3_scalar_multiply (color_result_vec3, HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR, color_result_vec3);
-
- egg_debug ("POST MULTIPLY: %s\n", gcm_vec3_to_string (color_result_vec3));
+ /* convert from device RGB to XYZ */
+ gcm_sensor_huey_convert_device_RGB_to_XYZ (&color_native,
+ &color_result,
+ device_calibration,
+ &sensor_huey->priv->dark_offset,
+ 2000.0f,
+ HUEY_XYZ_POST_MULTIPLY_SCALE_FACTOR);
/* save result */
tmp = g_new0 (GcmColorXYZ, 1);
@@ -1088,12 +1112,12 @@ gcm_sensor_huey_startup (GcmSensor *sensor, GError **error)
egg_debug ("device matrix2: %s", gcm_mat33_to_string (&priv->calibration_crt));
/* this number is different on all three hueys */
- ret = gcm_sensor_huey_read_register_float (sensor_huey, HUEY_EEPROM_ADDR_CALIBRATION_DATA_CRT, &priv->calibration_value, error);
+ ret = gcm_sensor_huey_read_register_float (sensor_huey, HUEY_EEPROM_ADDR_CALIB_VALUE, &priv->calibration_value, error);
if (!ret)
goto out;
/* this vector changes between sensor 1 and 3 */
- ret = gcm_sensor_huey_read_register_vector (sensor_huey, HUEY_EEPROM_ADDR_CALIBRATION_DATA_CRT, &priv->calibration_vector, error);
+ ret = gcm_sensor_huey_read_register_vector (sensor_huey, HUEY_EEPROM_ADDR_DARK_OFFSET, &priv->dark_offset, error);
if (!ret)
goto out;
@@ -1137,10 +1161,10 @@ gcm_sensor_huey_dump (GcmSensor *sensor, GString *data, GError **error)
g_string_append_printf (data, "huey-dump-version:%i\n", 2);
g_string_append_printf (data, "unlock-string:%s\n", priv->unlock_string);
g_string_append_printf (data, "calibration-value:%f\n", priv->calibration_value);
- g_string_append_printf (data, "calibration-vector:%f,%f,%f\n",
- priv->calibration_vector.v0,
- priv->calibration_vector.v1,
- priv->calibration_vector.v2);
+ g_string_append_printf (data, "dark-offset:%f,%f,%f\n",
+ priv->dark_offset.v0,
+ priv->dark_offset.v1,
+ priv->dark_offset.v2);
/* read all the register space */
for (i=0; i<0xff; i++) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]