[gimp-gap/gap-2-8] support colordiff DeltaE methods
- From: Wolfgang Hofer <wolfgangh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp-gap/gap-2-8] support colordiff DeltaE methods
- Date: Thu, 25 Jan 2018 13:01:09 +0000 (UTC)
commit e2510268dfbe77611e74672e8b659bd7d9c26ee4
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date: Thu Jan 25 14:00:42 2018 +0100
support colordiff DeltaE methods
ChangeLog | 14 ++
gap/gap_bluebox.c | 144 +++++++++++++++++-
gap/gap_bluebox.h | 4 +
gap/gap_bluebox_main.c | 2 +
gap/gap_colordiff.c | 353 ++++++++++++++++++++++++++++++++++++++++--
gap/gap_colordiff.h | 45 ++++++
gap/gap_story_render_audio.c | 6 +-
7 files changed, 552 insertions(+), 16 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 391fa21..eb7656c 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2018-01-25 Wolfgang Hofer <hof gimp org>
+
+- master video encoder provides more information in the error message
+ in case creating composite audiofile fails due to problems calling
+ the external audioconverter (sox)
+
+- Support L*A*B colorspace based colordiff methods
+ DelteE CIE94 and CIEDE2000 in the bluebox filter
+
+ * gap/gap_colordiff.c [.h]
+ * gap/gap_bluebox.c [.h]
+ * gap/gap_bluebox_main.c
+ * gap/gap_story_render_audio.c
+
2018-01-19 Wolfgang Hofer <hof gimp org>
- applied patch provided by Anders Jonsson @ https://bugzilla.gnome.org/show_bug.cgi?id=792549
diff --git a/gap/gap_bluebox.c b/gap/gap_bluebox.c
index 55d122f..8b041c4 100644
--- a/gap/gap_bluebox.c
+++ b/gap/gap_bluebox.c
@@ -54,6 +54,7 @@
#include "gap_lib.h"
#include "gap_pdb_calls.h"
#include "gap_bluebox.h"
+#include "gap_colordiff.h"
/* instant apply is implemented via timer, configured to fire 10 times per second (100 msec)
@@ -107,6 +108,8 @@ static void p_color_update_callback(GtkWidget *widget, gpointer val);
static void p_radio_thres_RGB_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp);
static void p_radio_thres_HSV_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp);
static void p_radio_thres_VAL_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp);
+static void p_radio_thres_E94_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp);
+static void p_radio_thres_E2000_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp);
static void p_radio_thres_ALL_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp);
static void p_radio_create_thres_mode(GtkWidget *table, int row, int col, GapBlueboxGlobalParams *bbp);
static void p_thres_table_RGB_create(GapBlueboxGlobalParams *bbp, gint row);
@@ -548,6 +551,14 @@ p_reset_callback(GtkWidget *w, GapBlueboxGlobalParams *bbp)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bbp->thres_val_toggle)
,radio_active);
+ radio_active = (bbp->vals.thres_mode == GAP_BLUBOX_THRES_DELTAE_CIE94);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bbp->thres_e94_toggle)
+ ,radio_active);
+
+ radio_active = (bbp->vals.thres_mode == GAP_BLUBOX_THRES_DELTAE_CIEDE200);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bbp->thres_e2000_toggle)
+ ,radio_active);
+
radio_active = (bbp->vals.thres_mode == GAP_BLUBOX_THRES_ALL);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bbp->thres_all_toggle)
,radio_active);
@@ -603,6 +614,8 @@ p_quit_callback(GtkWidget *w, GapBlueboxGlobalParams *bbp)
bbp->thres_rgb_toggle = NULL;
bbp->thres_hsv_toggle = NULL;
bbp->thres_val_toggle = NULL;
+ bbp->thres_e94_toggle = NULL;
+ bbp->thres_e2000_toggle = NULL;
bbp->thres_all_toggle = NULL;
/* p_quit_callback is the signal handler for the "destroy"
@@ -830,6 +843,34 @@ p_radio_thres_VAL_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp)
} /* end p_radio_thres_VAL_callback */
/* ---------------------------------
+ * p_radio_thres_E94_callback
+ * ---------------------------------
+ */
+static void
+p_radio_thres_E94_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp)
+{
+ if((bbp) && (GTK_TOGGLE_BUTTON (widget)->active))
+ {
+ bbp->vals.thres_mode = GAP_BLUBOX_THRES_DELTAE_CIE94;
+ p_thres_table_create_or_replace(bbp);
+ }
+} /* end p_radio_thres_E94_callback */
+
+/* ---------------------------------
+ * p_radio_thres_E2000_callback
+ * ---------------------------------
+ */
+static void
+p_radio_thres_E2000_callback(GtkWidget *widget, GapBlueboxGlobalParams *bbp)
+{
+ if((bbp) && (GTK_TOGGLE_BUTTON (widget)->active))
+ {
+ bbp->vals.thres_mode = GAP_BLUBOX_THRES_DELTAE_CIEDE200;
+ p_thres_table_create_or_replace(bbp);
+ }
+} /* end p_radio_thres_E2000_callback */
+
+/* ---------------------------------
* p_radio_thres_ALL_callback
* ---------------------------------
*/
@@ -924,15 +965,51 @@ p_radio_create_thres_mode(GtkWidget *table, int row, int col, GapBlueboxGlobalPa
G_CALLBACK (p_radio_thres_VAL_callback),
bbp);
-
l_idx = 3;
+ /* radio button thres_mode DeltaE CIE94 */
+ radio_button = gtk_radio_button_new_with_label ( radio_group, _("E94") );
+ radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
+ gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1, 0, 1
+ , GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ bbp->thres_e94_toggle = radio_button;
+
+ l_radio_pressed = (bbp->vals.thres_mode == GAP_BLUBOX_THRES_DELTAE_CIE94);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button),
+ l_radio_pressed);
+ gimp_help_set_help_data(radio_button, _("Use single threshold value DeltaE CIE94"), NULL);
+
+ gtk_widget_show (radio_button);
+ g_signal_connect ( G_OBJECT (radio_button), "toggled",
+ G_CALLBACK (p_radio_thres_E94_callback),
+ bbp);
+ l_idx = 4;
+
+ /* radio button thres_mode DeltaE CIEDE2000 */
+ radio_button = gtk_radio_button_new_with_label ( radio_group, _("E2000") );
+ radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
+ gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1, 0, 1
+ , GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ bbp->thres_e2000_toggle = radio_button;
+
+ l_radio_pressed = (bbp->vals.thres_mode == GAP_BLUBOX_THRES_DELTAE_CIEDE200);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button),
+ l_radio_pressed);
+ gimp_help_set_help_data(radio_button, _("Use single threshold value DeltaE CIEDE2000"), NULL);
+
+ gtk_widget_show (radio_button);
+ g_signal_connect ( G_OBJECT (radio_button), "toggled",
+ G_CALLBACK (p_radio_thres_E2000_callback),
+ bbp);
+
+ l_idx = 5;
+
/* radio button thres_mode ALL */
radio_button = gtk_radio_button_new_with_label ( radio_group, _("ALL") );
radio_group = gtk_radio_button_get_group ( GTK_RADIO_BUTTON (radio_button) );
gtk_table_attach ( GTK_TABLE (radio_table), radio_button, l_idx, l_idx+1, 0, 1
, GTK_FILL | GTK_EXPAND, 0, 0, 0);
- bbp->thres_val_toggle = radio_button;
+ bbp->thres_all_toggle = radio_button;
l_radio_pressed = (bbp->vals.thres_mode == GAP_BLUBOX_THRES_VAL);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button),
@@ -1139,6 +1216,9 @@ p_thres_table_create_or_replace(GapBlueboxGlobalParams *bbp)
p_thres_table_HSV_create(bbp, 0);
break;
case GAP_BLUBOX_THRES_VAL:
+ case GAP_BLUBOX_THRES_DELTAE_CIE94:
+ case GAP_BLUBOX_THRES_DELTAE_CIEDE200:
+ /* modes operating with a single threshold value */
p_thres_table_VAL_create(bbp);
break;
case GAP_BLUBOX_THRES_ALL:
@@ -1377,11 +1457,59 @@ p_check_VAL_thres (GimpRGB *src,
} /* end p_check_VAL_thres */
/* ---------------------------------
+ * p_check_DeltaE_thres
+ * ---------------------------------
+ */
+static inline gdouble
+p_check_DeltaE_thres (guchar *srcPixelPtr,
+ GapBlueboxGlobalParams *bbp)
+{
+ gdouble l_maxdiff;
+ guchar keycolor[4];
+
+ /* convert keycolor to uchar
+ * TODO: for better performance GapBlueboxGlobalParams shall provide a L*A*B colorspace representation
(of type GapColorLAB)
+ * of the keycolor, and there should be additional colordiff methods capable to compare uchar RGB 255
versus GapColorLAB
+ */
+ gimp_rgba_get_uchar (&bbp->vals.keycolor, &keycolor[0], &keycolor[1], &keycolor[2], &keycolor[3]);
+
+ if (bbp->vals.thres_mode == GAP_BLUBOX_THRES_DELTAE_CIE94)
+ {
+ l_maxdiff = gap_colordiff_LabDeltaE94(srcPixelPtr
+ , &keycolor[0]
+ , FALSE /* gboolean debugPrint */
+ );
+ }
+ else
+ {
+ l_maxdiff = gap_colordiff_LabDeltaE2000(srcPixelPtr
+ , &keycolor[0]
+ , FALSE /* gboolean debugPrint */
+ );
+ }
+
+ if(l_maxdiff > bbp->vals.thres) { return 1.0; }
+
+ if(bbp->vals.tolerance > 0)
+ {
+ gdouble aa;
+
+ aa = (l_maxdiff / bbp->vals.thres);
+ if (aa >= bbp->inv_tolerance)
+ {
+ return ((aa - bbp->inv_tolerance) * (1 / bbp->inv_tolerance));
+ }
+ }
+
+ return 0.0;
+} /* end p_check_DeltaE_thres */
+
+/* ---------------------------------
* p_pixel_render_alpha
* ---------------------------------
*/
static inline void
-p_pixel_render_alpha (GimpRGB *src,
+p_pixel_render_alpha (GimpRGB *src, guchar *srcPixelPtr,
GapBlueboxGlobalParams *bbp)
{
if(src->a >= bbp->vals.source_alpha)
@@ -1399,6 +1527,12 @@ p_pixel_render_alpha (GimpRGB *src,
case GAP_BLUBOX_THRES_VAL:
col_diff = p_check_VAL_thres(src, bbp);
break;
+ case GAP_BLUBOX_THRES_DELTAE_CIE94:
+ col_diff = p_check_DeltaE_thres(srcPixelPtr, bbp);
+ break;
+ case GAP_BLUBOX_THRES_DELTAE_CIEDE200:
+ col_diff = p_check_DeltaE_thres(srcPixelPtr, bbp);
+ break;
case GAP_BLUBOX_THRES_ALL:
col_diff = MAX(p_check_HSV_thres(src, bbp), p_check_RGB_thres(src, bbp));
break;
@@ -1425,9 +1559,9 @@ p_toalpha_func (const guchar *src,
GapBlueboxGlobalParams *bbp;
bbp = (GapBlueboxGlobalParams *)data;
-
+
gimp_rgba_set_uchar (&color, src[0], src[1], src[2], src[3]);
- p_pixel_render_alpha (&color, bbp);
+ p_pixel_render_alpha (&color, src, bbp);
gimp_rgba_get_uchar (&color, &dest[0], &dest[1], &dest[2], &dest[3]);
} /* end p_toalpha_func */
diff --git a/gap/gap_bluebox.h b/gap/gap_bluebox.h
index a4f8416..cd5267a 100644
--- a/gap/gap_bluebox.h
+++ b/gap/gap_bluebox.h
@@ -42,6 +42,8 @@ typedef enum
,GAP_BLUBOX_THRES_HSV
,GAP_BLUBOX_THRES_VAL
,GAP_BLUBOX_THRES_ALL
+ ,GAP_BLUBOX_THRES_DELTAE_CIE94
+ ,GAP_BLUBOX_THRES_DELTAE_CIEDE200
} GapBlueboxThresMode;
typedef struct GapBlueboxVals {
@@ -107,6 +109,8 @@ typedef struct GapBlueboxGlobalParams {
GtkWidget *thres_rgb_toggle;
GtkWidget *thres_hsv_toggle;
GtkWidget *thres_val_toggle;
+ GtkWidget *thres_e94_toggle;
+ GtkWidget *thres_e2000_toggle;
GtkWidget *thres_all_toggle;
GdkCursor *cursor_wait;
GdkCursor *cursor_acitve;
diff --git a/gap/gap_bluebox_main.c b/gap/gap_bluebox_main.c
index d87d072..756f868 100644
--- a/gap/gap_bluebox_main.c
+++ b/gap/gap_bluebox_main.c
@@ -70,6 +70,8 @@ static GimpParamDef args_bluebox[] =
{GIMP_PDB_INT32, "thres_mode", "0 .. use the 3 threshold values for RGB\n"
"1 .. use the 3 threshold values for HSV\n"
"2 .. use only one simple Threshold\n"
+ "4 .. use only one simple Threshold DeltaE CIE94\n"
+ "5 .. use only one simple Threshold DeltaE CIEDE2000\n"
"3 .. use all 6 threshold values for HSV and RGB"},
{GIMP_PDB_FLOAT, "thres_r", "threshold value 0.0 upto 1.0 for RED value (ignored in thers_modes 1 and
2)"},
{GIMP_PDB_FLOAT, "thres_g", "threshold value 0.0 upto 1.0 for GREEN value (ignored in thers_modes 1 and
2)"},
diff --git a/gap/gap_colordiff.c b/gap/gap_colordiff.c
index f827660..cbf68e5 100644
--- a/gap/gap_colordiff.c
+++ b/gap/gap_colordiff.c
@@ -44,6 +44,8 @@
extern int gap_debug; /* ==0 ... dont print debug infos */
+static gdouble p_PivotRgb(gdouble n);
+static gdouble p_PivotXyz(gdouble n);
// static long p_gdouble_to_long(gdouble value)
@@ -345,16 +347,16 @@ gap_colordiff_simple_guchar(guchar *aPixelPtr
colorDiff = (gdouble)(rDif + gDif + bDif) / MAX_CHANNEL_SUM;
if(debugPrint)
{
- printf("rgb 1/2 (%.3g %.3g %.3g) / (%.3g %.3g %.3g) rDif:%.3g gDif:%.3g bDif:%.3g colorDiff:%f\n"
- , (float)(aPixelPtr[0]) / 255.0
- , (float)(aPixelPtr[1]) / 255.0
- , (float)(aPixelPtr[2]) / 255.0
- , (float)(bPixelPtr[0]) / 255.0
- , (float)(bPixelPtr[1]) / 255.0
- , (float)(bPixelPtr[2]) / 255.0
- , (float)(rDif) / 255.0
- , (float)(gDif) / 255.0
- , (float)(bDif) / 255.0
+ printf("Simple rgb 1/2 (%03d %03d %03d) / (%03d %03d %03d) rDif:%03d gDif:%03d bDif:%03d colorDiff:%f\n"
+ , (int)(aPixelPtr[0])
+ , (int)(aPixelPtr[1])
+ , (int)(aPixelPtr[2])
+ , (int)(bPixelPtr[0])
+ , (int)(bPixelPtr[1])
+ , (int)(bPixelPtr[2])
+ , (int)(rDif)
+ , (int)(gDif)
+ , (int)(bDif)
, colorDiff
);
}
@@ -369,6 +371,8 @@ gap_colordiff_simple_guchar(guchar *aPixelPtr
* ---------------------------------
* returns difference of 2 colors as gdouble value
* in range 0.0 (exact match) to 1.0 (maximal difference)
+ *
+ * DEPRECATED
*/
gdouble
gap_colordiff_hvmax_GimpHSV(GimpHSV *aHsvPtr
@@ -458,6 +462,8 @@ gap_colordiff_hvmax_GimpHSV(GimpHSV *aHsvPtr
* this procedure uses an HSV colormodel based
* Algorithm and calculates difference as max HSV difference.
*
+ * DEPRECATED
+ *
*/
gdouble
gap_colordiff_hvmax_guchar(guchar *aPixelPtr
@@ -483,3 +489,330 @@ gap_colordiff_hvmax_guchar(guchar *aPixelPtr
} /* end gap_colordiff_hvmax_guchar */
+
+
+/* #########################################
+ * LAB based methods
+ * #########################################
+ *
+ * this code uses the L*A*B conversion methods
+ * found at
https://github.com/THEjoezack/ColorMine/blob/master/ColorMine/ColorSpaces/Conversions/LabConverter.cs
+ * ported to C.
+ * because:
+ * - they fit the colordiff methods from same source.
+ * - did not check yet for possible L*A*B support available in GIMP-2.9
+ * (because this code is supposed to run with GIMP-2.8)
+ *
+ */
+
+/* ---------------------------------
+ * p_PivotRgb
+ * ---------------------------------
+ */
+static gdouble p_PivotRgb(gdouble n)
+{
+ gdouble ret;
+ if (n > 0.04045)
+ {
+ ret = pow((n + 0.055) / 1.055, 2.4);
+ }
+ else
+ {
+ ret = n / 12.92;
+ }
+ return (ret * 100.0);
+
+} /* end p_PivotRgb */
+
+/* ---------------------------------
+ * p_PivotXyz
+ * ---------------------------------
+ */
+static gdouble p_PivotXyz(gdouble n)
+{
+#define XYZCONVERTER_EPSILON 0.008856 /* Intent is 216/24389 */
+#define XYZCONVERTER_KAPPA 903.3 /* Intent is 24389/27 */
+ gdouble ret;
+ if (n > XYZCONVERTER_EPSILON)
+ {
+ /* ret = CubicRoot(n) */
+ ret = pow(n, 1.0 / 3.0);
+ }
+ else
+ {
+ ret = (XYZCONVERTER_KAPPA * n + 16.0) / 116.0;
+ }
+ return ret;
+
+} /* end p_PivotXyz */
+
+
+/* ---------------------------------
+ * p_convert_rgb8_to_Lab
+ * ---------------------------------
+ * based on algorithm found at
+ * https://github.com/THEjoezack/ColorMine/blob/master/ColorMine/ColorSpaces/Conversions/LabConverter.cs
+ * https://github.com/THEjoezack/ColorMine/blob/master/ColorMine/ColorSpaces/Conversions/XyzConverter.cs
+ *
+ */
+void p_convert_rgb8_to_Lab(guchar *pixelPtrRgb8, GapColorLAB *lab)
+{
+ /* XYZ colorspace constants */
+ #define WHITE_X 95.047
+ #define WHITE_Y 100.0
+ #define WHITE_Z 108.883
+
+ gdouble xyzX,xyzY,xyzZ;
+ gdouble x,y,z;
+ gdouble r,g,b;
+
+ r = p_PivotRgb((gdouble)pixelPtrRgb8[0] / 255.0);
+ g = p_PivotRgb((gdouble)pixelPtrRgb8[1] / 255.0);
+ b = p_PivotRgb((gdouble)pixelPtrRgb8[2] / 255.0);
+
+ /* Observer. = 2°, Illuminant = D65 */
+ xyzX = r * 0.4124 + g * 0.3576 + b * 0.1805;
+ xyzY = r * 0.2126 + g * 0.7152 + b * 0.0722;
+ xyzZ = r * 0.0193 + g * 0.1192 + b * 0.9505;
+
+ x = p_PivotXyz(xyzX / WHITE_X);
+ y = p_PivotXyz(xyzY / WHITE_Y);
+ z = p_PivotXyz(xyzZ / WHITE_Z);
+
+
+ lab->L = MAX(0.0, 116.0 * y - 16.0);
+ lab->A = 500.0 * (x - y);
+ lab->B = 200.0 * (y - z);
+
+} /* end p_convert_rgb8_to_Lab */
+
+
+
+/* ---------------------------------
+ * gap_colordiff_LabDeltaE2000
+ * ---------------------------------
+ * uses algortihm found at http://colormine.org/delta-e-calculator/cie2000
+ * based on L*a*b Colorspace.
+ * returns deltaE scaled down to a range of 0.0 to 1.0
+ */
+gdouble gap_colordiff_LabDeltaE2000(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gboolean debugPrint
+ )
+{
+ //Set weighting factors to 1
+ gdouble k_L = 1.0;
+ gdouble k_C = 1.0;
+ gdouble k_H = 1.0;
+
+
+ GapColorLAB lab1;
+ GapColorLAB lab2;
+
+ //Change Color Space to L*a*b:
+ p_convert_rgb8_to_Lab(aPixelPtr, &lab1);
+ p_convert_rgb8_to_Lab(bPixelPtr, &lab2);
+
+
+ //Calculate Cprime1, Cprime2, Cabbar
+ gdouble c_star_1_ab = sqrt(lab1.A * lab1.A + lab1.B * lab1.B);
+ gdouble c_star_2_ab = sqrt(lab2.A * lab2.A + lab2.B * lab2.B);
+ gdouble c_star_average_ab = (c_star_1_ab + c_star_2_ab) / 2;
+
+ gdouble c_star_average_ab_pot7 = c_star_average_ab * c_star_average_ab * c_star_average_ab;
+ c_star_average_ab_pot7 *= c_star_average_ab_pot7 * c_star_average_ab;
+
+ gdouble G = 0.5 * (1 - sqrt(c_star_average_ab_pot7 / (c_star_average_ab_pot7 + 6103515625))); //25^7
+ gdouble a1_prime = (1 + G) * lab1.A;
+ gdouble a2_prime = (1 + G) * lab2.A;
+
+ gdouble C_prime_1 = sqrt(a1_prime * a1_prime + lab1.B * lab1.B);
+ gdouble C_prime_2 = sqrt(a2_prime * a2_prime + lab2.B * lab2.B);
+ //Angles in Degree.
+ gdouble h_prime_1 = (int)rint(gimp_rad_to_deg(atan2(lab1.B, a1_prime)) + 360) % 360;
+ gdouble h_prime_2 = (int)rint(gimp_rad_to_deg(atan2(lab2.B, a2_prime)) + 360) % 360;
+
+ gdouble delta_L_prime = lab2.L - lab1.L;
+ gdouble delta_C_prime = C_prime_2 - C_prime_1;
+
+ gdouble h_bar = abs(h_prime_1 - h_prime_2);
+ gdouble delta_h_prime;
+ if (C_prime_1 * C_prime_2 == 0) delta_h_prime = 0;
+ else
+ {
+ if (h_bar <= 180.0)
+ {
+ delta_h_prime = h_prime_2 - h_prime_1;
+ }
+ else if (h_bar > 180.0 && h_prime_2 <= h_prime_1)
+ {
+ delta_h_prime = h_prime_2 - h_prime_1 + 360.0;
+ }
+ else
+ {
+ delta_h_prime = h_prime_2 - h_prime_1 - 360.0;
+ }
+ }
+ gdouble delta_H_prime = 2 * sqrt(C_prime_1 * C_prime_2) * sin(gimp_deg_to_rad(delta_h_prime / 2));
+
+ // Calculate CIEDE2000
+ gdouble L_prime_average = (lab1.L + lab2.L) / 2.0;
+ gdouble C_prime_average = (C_prime_1 + C_prime_2) / 2.0;
+
+ //Calculate h_prime_average
+
+ gdouble h_prime_average;
+ if (C_prime_1 * C_prime_2 == 0) h_prime_average = 0;
+ else
+ {
+ if (h_bar <= 180.0)
+ {
+ h_prime_average = (h_prime_1 + h_prime_2) / 2;
+ }
+ else if (h_bar > 180.0 && (h_prime_1 + h_prime_2) < 360.0)
+ {
+ h_prime_average = (h_prime_1 + h_prime_2 + 360.0) / 2;
+ }
+ else
+ {
+ h_prime_average = (h_prime_1 + h_prime_2 - 360.0) / 2;
+ }
+ }
+ gdouble L_prime_average_minus_50_square = (L_prime_average - 50);
+ L_prime_average_minus_50_square *= L_prime_average_minus_50_square;
+
+ gdouble S_L = 1 + ((.015d * L_prime_average_minus_50_square) / sqrt(20 + L_prime_average_minus_50_square));
+ gdouble S_C = 1 + .045d * C_prime_average;
+ gdouble T = 1
+ - .17 * cos(gimp_deg_to_rad(h_prime_average - 30))
+ + .24 * cos(gimp_deg_to_rad(h_prime_average * 2))
+ + .32 * cos(gimp_deg_to_rad(h_prime_average * 3 + 6))
+ - .2 * cos(gimp_deg_to_rad(h_prime_average * 4 - 63));
+ gdouble S_H = 1 + .015 * T * C_prime_average;
+ gdouble h_prime_average_minus_275_div_25_square = (h_prime_average - 275) / (25);
+ h_prime_average_minus_275_div_25_square *= h_prime_average_minus_275_div_25_square;
+ gdouble delta_theta = 30 * exp(-h_prime_average_minus_275_div_25_square);
+
+ gdouble C_prime_average_pot_7 = C_prime_average * C_prime_average * C_prime_average;
+ C_prime_average_pot_7 *= C_prime_average_pot_7 * C_prime_average;
+ gdouble R_C = 2 * sqrt(C_prime_average_pot_7 / (C_prime_average_pot_7 + 6103515625));
+
+ gdouble R_T = -sin(gimp_deg_to_rad(2 * delta_theta)) * R_C;
+
+ gdouble delta_L_prime_div_k_L_S_L = delta_L_prime / (S_L * k_L);
+ gdouble delta_C_prime_div_k_C_S_C = delta_C_prime / (S_C * k_C);
+ gdouble delta_H_prime_div_k_H_S_H = delta_H_prime / (S_H * k_H);
+
+ gdouble CIEDE2000 = sqrt(
+ delta_L_prime_div_k_L_S_L * delta_L_prime_div_k_L_S_L
+ + delta_C_prime_div_k_C_S_C * delta_C_prime_div_k_C_S_C
+ + delta_H_prime_div_k_H_S_H * delta_H_prime_div_k_H_S_H
+ + R_T * delta_C_prime_div_k_C_S_C * delta_H_prime_div_k_H_S_H
+ );
+
+
+ gdouble colorDiff = CIEDE2000 / 100.0; /* normalize range [from 0.0 to 100.0] ==> [from 0.0 to 1.0] */
+ if(debugPrint)
+ {
+ printf("L*a*b (%.3f, %.3f, %.3f) rgb 1/2 (%03d %03d %03d) / (%03d %03d %03d) CIEDE2000:%f colorDiff:%f\n"
+ , lab1.L
+ , lab1.A
+ , lab1.B
+ , (int)(aPixelPtr[0])
+ , (int)(aPixelPtr[1])
+ , (int)(aPixelPtr[2])
+ , (int)(bPixelPtr[0])
+ , (int)(bPixelPtr[1])
+ , (int)(bPixelPtr[2])
+ , CIEDE2000
+ , colorDiff
+ );
+ }
+
+ return (colorDiff);
+
+} /* end gap_colordiff_LabDeltaE2000 */
+
+
+/* ---------------------------------
+ * gap_colordiff_LabDeltaE94
+ * ---------------------------------
+ * uses algortihm found at http://colormine.org/delta-e-calculator/cie94
+ * based on L*a*b Colorspace.
+ * returns deltaE scaled down to a range of 0.0 to 1.0
+ */
+gdouble gap_colordiff_LabDeltaE94(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gboolean debugPrint
+ )
+{
+ // case Application.GraphicArts:
+ gdouble CONSTANTS_Kl = 1.0;
+ gdouble CONSTANTS_K1 = .045;
+ gdouble CONSTANTS_K2 = .015;
+
+ // case Application.Textiles:
+ //gdouble CONSTANTS_Kl = 2.0;
+ //gdouble CONSTANTS_K1 = .048;
+ //gdouble CONSTANTS_K2 = .014;
+
+ gdouble sl = 1.0;
+ gdouble kc = 1.0;
+ gdouble kh = 1.0;
+
+
+ GapColorLAB labA;
+ GapColorLAB labB;
+
+ //Change Color Space to L*a*b:
+ p_convert_rgb8_to_Lab(aPixelPtr, &labA);
+ p_convert_rgb8_to_Lab(bPixelPtr, &labB);
+
+ gdouble deltaL = labA.L - labB.L;
+ gdouble deltaA = labA.A - labB.A;
+ gdouble deltaB = labA.B - labB.B;
+
+ gdouble c1 = sqrt(labA.A * labA.A + labA.B * labA.B);
+ gdouble c2 = sqrt(labB.A * labB.A + labB.B * labB.B);
+ gdouble deltaC = c1 - c2;
+
+ gdouble deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
+ deltaH = deltaH < 0 ? 0 : sqrt(deltaH);
+
+
+ gdouble sc = 1.0 + CONSTANTS_K1 * c1;
+ gdouble sh = 1.0 + CONSTANTS_K2 * c1;
+
+ gdouble deltaLKlsl = deltaL / (CONSTANTS_Kl * sl);
+ gdouble deltaCkcsc = deltaC / (kc * sc);
+ gdouble deltaHkhsh = deltaH / (kh * sh);
+ gdouble i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh;
+
+ gdouble cie94 = (i < 0) ? 0 : sqrt(i);
+
+ gdouble colorDiff = cie94 / 100.0; /* normalize range [from 0.0 to 100.0] ==> [from 0.0 to 1.0] */
+ if(debugPrint)
+ {
+ printf("L*a*b (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f) rgb 1/2 (%03d %03d %03d) / (%03d %03d %03d) cie94:%f
colorDiff:%f\n"
+ , labA.L
+ , labA.A
+ , labA.B
+ , labB.L
+ , labB.A
+ , labB.B
+ , (int)(aPixelPtr[0])
+ , (int)(aPixelPtr[1])
+ , (int)(aPixelPtr[2])
+ , (int)(bPixelPtr[0])
+ , (int)(bPixelPtr[1])
+ , (int)(bPixelPtr[2])
+ , cie94
+ , colorDiff
+ );
+ }
+
+ return (colorDiff);
+
+
+} /* end gap_colordiff_LabDeltaE94 */
+
diff --git a/gap/gap_colordiff.h b/gap/gap_colordiff.h
index 651671d..140b8d3 100644
--- a/gap/gap_colordiff.h
+++ b/gap/gap_colordiff.h
@@ -40,6 +40,13 @@
#define GAP_COLORDIFF_DEFAULT_SENSITIVITY 1.35
+
+typedef struct GapColorLAB {
+ gdouble L;
+ gdouble A;
+ gdouble B;
+} GapColorLAB;
+
/* ---------------------------------
* gap_colordiff_GimpHSV
* ---------------------------------
@@ -151,6 +158,8 @@ gap_colordiff_simple_guchar(guchar *aPixelPtr
* Note:
* this procedure uses an HSV colormodel based
* Algorithm and calculates difference as max HSV difference.
+ *
+ * DEPRECATED
*
*/
gdouble
@@ -165,11 +174,47 @@ gap_colordiff_hvmax_guchar(guchar *aPixelPtr
* ---------------------------------
* returns difference of 2 colors as gdouble value
* in range 0.0 (exact match) to 1.0 (maximal difference)
+ *
+ * DEPRECATED
*/
gdouble
gap_colordiff_hvmax_GimpHSV(GimpHSV *aHsvPtr
, GimpHSV *bHsvPtr
, gboolean debugPrint);
+
+
+
+
+/* ---------------------------------
+ * p_convert_rgb8_to_Lab
+ * ---------------------------------
+ *
+ */
+void
+p_convert_rgb8_to_Lab(guchar *pixelPtrRgb8, GapColorLAB *lab);
+
+
+/* ---------------------------------
+ * gap_colordiff_LabDeltaE2000
+ * ---------------------------------
+ *
+ */
+gdouble
+gap_colordiff_LabDeltaE2000(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gboolean debugPrint
+ );
+
+/* ---------------------------------
+ * gap_colordiff_LabDeltaE94
+ * ---------------------------------
+ *
+ */
+gdouble
+gap_colordiff_LabDeltaE94(guchar *aPixelPtr
+ , guchar *bPixelPtr
+ , gboolean debugPrint
+ );
#endif
diff --git a/gap/gap_story_render_audio.c b/gap/gap_story_render_audio.c
index a45e845..dc4749c 100644
--- a/gap/gap_story_render_audio.c
+++ b/gap/gap_story_render_audio.c
@@ -1506,7 +1506,11 @@ gap_story_render_audio_new_audiorange_element(GapStoryRenderAudioType aud_type
aud_elem->tmp_audiofile = NULL;
}
- l_errtxt = g_strdup_printf(_("cant use file: %s as audioinput"), aud_elem->audiofile);
+ l_errtxt = g_strdup_printf(_("cant use file: %s as audioinput\n"
+ "(external converter %s FAILED to resample as WAV format at target samplerate %d)")
+ , aud_elem->audiofile
+ , util_sox
+ , master_samplerate);
gap_story_render_set_stb_error(sterr, l_errtxt);
g_free(l_errtxt);
if(gap_debug)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]