diff --git a/README.md b/README.md index cd5482c..689c533 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,13 @@ Scores for version 1.1 measured against [TID2008][1] database: TID2008 Category | Spearman correlation --- | --- -Noise | -0.866 -Noise2 | -0.882 -Safe | -0.884 +Noise | -0.872 +Noise2 | -0.888 +Safe | -0.889 Hard | -0.903 -Simple | -0.921 -Exotic | -0.449 -Exotic2 | -0.620 -Full | -0.804 +Simple | -0.922 +Exotic | -0.484 +Exotic2 | -0.651 +Full | -0.818 [1]: http://www.computervisiononline.com/dataset/tid2008-tampere-image-database-2008 diff --git a/src/dssim.c b/src/dssim.c index fb02453..cdd5a96 100644 --- a/src/dssim.c +++ b/src/dssim.c @@ -170,10 +170,25 @@ void dssim_dealloc_image(dssim_image *img) free(img); } -static void set_gamma(double gamma_lut[static 256], const double invgamma) +static int set_gamma(double gamma_lut[static 256], const double invgamma) { - for (int i = 0; i < 256; i++) { - gamma_lut[i] = pow(i / 255.0, 1.0 / invgamma); + if (invgamma == dssim_srgb_gamma) { + for (int i = 0; i < 256; i++) { + const double s = i / 255.0; + if (s <= 0.04045) { + gamma_lut[i] = s / 12.92; + } else { + gamma_lut[i] = pow((s + 0.055) / 1.055, 2.4); + } + } + return 1; + } else if (invgamma > 0 && invgamma < 1.0) { + for (int i = 0; i < 256; i++) { + gamma_lut[i] = pow(i / 255.0, 1.0 / invgamma); + } + return 1; + } else { + return 0; } } @@ -202,6 +217,7 @@ inline static dssim_lab rgb_to_lab(const double gamma_lut[static 256], const uns }; } +#ifndef USE_COCOA /* * Flips x/y (like 90deg rotation) */ @@ -267,7 +283,7 @@ static void regular_1d_blur(const dssim_px_t *src, dssim_px_t *restrict tmp1, ds } } } - +#endif /* * blurs (approximate of gaussian) @@ -478,14 +494,13 @@ dssim_image *dssim_create_image(dssim_attr *attr, unsigned char *const *const ro dssim_row_callback *converter; int num_channels; - if (gamma <= 0 || gamma > 1.0) { - return NULL; - } - image_data im = { .row_pointers = (const unsigned char *const *const )row_pointers, }; - set_gamma(im.gamma_lut, gamma); + + if (!set_gamma(im.gamma_lut, gamma)) { + return NULL; + } switch(color_type) { case DSSIM_GRAY: diff --git a/src/dssim.h b/src/dssim.h index d39f641..f1ef005 100644 --- a/src/dssim.h +++ b/src/dssim.h @@ -43,6 +43,9 @@ typedef struct { dssim_attr *dssim_create_attr(void); void dssim_dealloc_attr(dssim_attr *); +// Magic number to use in place of gamma for a true sRGB curve +static const double dssim_srgb_gamma = -47571492; + /* Number of scales for multiscale (1 = regular SSIM). Optional weights array contains weight of each scale. Set before creating any images. diff --git a/src/main.c b/src/main.c index 74c2a55..f4f144d 100644 --- a/src/main.c +++ b/src/main.c @@ -88,8 +88,17 @@ inline static unsigned char to_byte(float in) { return in * 256.f; } -double get_gamma(double gamma) { +double get_gamma(const png24_image *image) { + // Assume unlabelled are sRGB too + if (RWPNG_NONE == image->output_color || RWPNG_SRGB == image->output_color) { + return dssim_srgb_gamma; + } + const double gamma = image->gamma; if (gamma > 0 && gamma <= 1.0) { + // If the gamma chunk states gamma closest to sRGB that PNG can express, then assume sRGB too + if (RWPNG_GAMA_ONLY == image->output_color && gamma > 0.4545499 && gamma < 0.4545501) { + return dssim_srgb_gamma; + } return gamma; } @@ -137,7 +146,7 @@ int main(int argc, char *const argv[]) dssim_attr *attr = dssim_create_attr(); - dssim_image *original = dssim_create_image(attr, image1.row_pointers, DSSIM_RGBA, image1.width, image1.height, get_gamma(image1.gamma)); + dssim_image *original = dssim_create_image(attr, image1.row_pointers, DSSIM_RGBA, image1.width, image1.height, get_gamma(&image1)); free(image1.row_pointers); free(image1.rgba_data); @@ -156,7 +165,7 @@ int main(int argc, char *const argv[]) break; } - dssim_image *modified = dssim_create_image(attr, image2.row_pointers, DSSIM_RGBA, image2.width, image2.height, get_gamma(image2.gamma)); + dssim_image *modified = dssim_create_image(attr, image2.row_pointers, DSSIM_RGBA, image2.width, image2.height, get_gamma(&image2)); free(image2.row_pointers); free(image2.rgba_data);