Ejemplo n.º 1
0
int main(int argc, char *const argv[])
{
    char *map_output_file = NULL;

    if (argc < 3) {
        usage(argv[0]);
        return 1;
    }

    opterr = 0;
    int c;
    while((c = getopt(argc, argv, "ho:")) != -1) {
        switch (c) {
            case 'h':
                usage(argv[0]);
                return 0;
            case 'o':
                map_output_file = optarg;
                break;
            default:
                fprintf(stderr, "Unknown option\n");
                return 1;
        }
    }

    if (optind+1 >= argc) {
        fprintf(stderr, "You must specify at least 2 files to compare\n");
        return 1;
    }

    const char *file1 = argv[optind];
    png24_image image1 = {};
    int retval = read_image(file1, &image1);
    if (retval) {
        fprintf(stderr, "Can't read %s\n", file1);
        return retval;
    }

    dssim_image *original = dssim_create_image(image1.row_pointers, DSSIM_RGBA, image1.width, image1.height, image1.gamma);
    free(image1.row_pointers);
    free(image1.rgba_data);

    for (int arg = optind+1; arg < argc; arg++) {
        const char *file2 = argv[arg];

        png24_image image2 = {};
        retval = read_image(file2, &image2);
        if (retval) {
            fprintf(stderr, "Can't read %s\n", file2);
            break;
        }

        if (image1.width != image2.width || image1.height != image2.height) {
            fprintf(stderr, "Image %s has different size than %s\n", file2, file1);
            break;
        }

        dssim_image *modified = dssim_create_image(image2.row_pointers, DSSIM_RGBA, image2.width, image2.height, image2.gamma);
        free(image2.row_pointers);
        free(image2.rgba_data);

        float *map = NULL;
        double dssim = dssim_compare(original, modified, map_output_file ? &map : NULL);
        dssim_dealloc_image(modified);

        printf("%.6f\t%s\n", dssim, file2);

        if (map) {
            dssim_rgba *out = (dssim_rgba*)map;
            for(int i=0; i < image2.width*image2.height; i++) {
                const float max = 1.0 - map[i];
                const float maxsq = max * max;
                out[i] = (dssim_rgba) {
                    .r = to_byte(max * 3.0),
                    .g = to_byte(maxsq * 3.0),
                    .b = to_byte((max-0.5) * 2.0f),
                    .a = 255,
                };
            }
            if (write_image(map_output_file, out, image2.width, image2.height)) {
                fprintf(stderr, "Can't write %s\n", map_output_file);
                free(map);
                return 1;
            }
            free(map);
        }
    }

    dssim_dealloc_image(original);
    return retval;
}
Ejemplo n.º 2
0
Archivo: imgmin.c Proyecto: dpq/imgmin
/*
 * given a source image, a destination filepath and a set of image metadata thresholds,
 * search for the lowest-quality version of the source image whose properties fall within our
 * thresholds.
 * this will produce an image file that looks the same to the casual observer, but which
 * contains much less information and results in a smaller file.
 * typical savings on unoptimized images vary widely from 10-80%, with 25-50% being most common.
 */
MagickWand * search_quality(MagickWand *mw, const char *dst,
                                   const struct imgmin_options *opt)
{
    MagickWand *tmp = NULL;
    char tmpfile[MAX_PATH] = "/tmp/imgminXXXXXX";
    if (0 == strcmp("-", dst))
    {
        if (-1 == mkstemp(tmpfile))
        {
            perror("mkstemp");
            return CloneMagickWand(mw);
        }
    } else {
        strcpy(tmpfile, dst);
    }

    /*
     * The overwhelming majority of JPEGs are TrueColorType; it is those types, with a low
     * unique color count, that we must avoid.
     */
    if (!enough_colors(mw, opt))
    {
        fprintf(stdout, " Color count is too low, skipping...\n");
        return CloneMagickWand(mw);
    }

    if (quality(mw) < opt->quality_in_min)
    {
        fprintf(stdout, " Quality < %u, won't second-guess...\n", opt->quality_in_min);
        return CloneMagickWand(mw);
    }

    size_t width = MagickGetImageWidth(mw);
    size_t height = MagickGetImageHeight(mw);

    dssim_info *dssim = dssim_init(1);

    void *convert_data = convert_row_start(mw);
    dssim_set_original_float_callback(dssim, width, height, convert_row_callback, convert_data);
    convert_row_finish(convert_data);

    {
        ExceptionInfo *exception = AcquireExceptionInfo();

        const double original_density = color_density(mw);
        unsigned qmax = min(quality(mw), opt->quality_out_max);
        unsigned qmin = opt->quality_out_min;
        unsigned steps = 0;

        /*
         * binary search of quality space for optimally lowest quality that
         * produces an acceptable level of distortion
         */
        while (qmax > qmin + 1 && steps < opt->max_steps)
        {
            double density_ratio;
            unsigned q;

            steps++;
            q = (qmax + qmin) / 2;

            /* change quality */
            tmp = CloneMagickWand(mw);
            MagickSetImageCompressionQuality(tmp, q);

            /* apply quality change */
            MagickWriteImages(tmp, tmpfile, MagickTrue);
            DestroyMagickWand(tmp);
            tmp = NewMagickWand();
            MagickReadImage(tmp, tmpfile);

            void *convert_data = convert_row_start(tmp);
            dssim_set_modified_float_callback(dssim, width, height, convert_row_callback, convert_data);
            convert_row_finish(convert_data);

            double error = 20.0 * dssim_compare(dssim, NULL); // scaled to threshold of previous implementation

            density_ratio = fabs(color_density(tmp) - original_density) / original_density;

            /* color density ratio threshold is an alternative quality measure.
               If it's exceeded, pretend MSE was higher to increase quality */
            if (density_ratio > opt->color_density_ratio) {
                error *= 1.25 + density_ratio; // fudge factor
            }

            /* eliminate half search space based on whether distortion within thresholds */
            if (error > opt->error_threshold)
            {
                qmin = q;
            } else {
                qmax = q;
            }
            if (opt->show_progress)
            {
                fprintf(stdout, "%.2f/%.2f@%u ", error, density_ratio, q);
            }

            /* Stop searching if close enough to the target */
            if (fabs(error - opt->error_threshold) < opt->error_threshold * ERROR_THRESHOLD_INACCURACY) {
                qmax = q;
                break;
            }
        }
        if (opt->show_progress)
        {
            putc('\n', stdout);
        }

        MagickSetImageCompressionQuality(mw, qmax);

        /* "Chroma sub-sampling works because human vision is relatively insensitive to
         * small areas of colour. It gives a significant reduction in file sizes, with
         * little loss of perceived quality." [3]
         */
#if MagickLibVersion >= 0x630 /* FIXME: available in 0x660, not available in 0x628, not sure which version it was introduced in */
        (void) MagickSetImageProperty(mw, "jpeg:sampling-factor", "2x2");
#endif

        /* strip an image of all profiles and comments */
        (void) MagickStripImage(mw);

        MagickWriteImages(mw, tmpfile, MagickTrue);
        (void) DestroyMagickWand(tmp);
        tmp = NewMagickWand();
        MagickReadImage(tmp, tmpfile);

        exception = DestroyExceptionInfo(exception);
    }

    return tmp;
}
Ejemplo n.º 3
0
Archivo: main.c Proyecto: pornel/dssim
int main(int argc, char *const argv[])
{
    char *map_output_file = NULL;

    if (argc < 3) {
        usage(argv[0]);
        return 1;
    }

    opterr = 0;
    int c;
    while((c = getopt(argc, argv, "ho:")) != -1) {
        switch (c) {
        case 'h':
            usage(argv[0]);
            return 0;
        case 'o':
            map_output_file = optarg;
            break;
        default:
            fprintf(stderr, "Unknown option\n");
            return 1;
        }
    }

    if (optind+1 >= argc) {
        fprintf(stderr, "You must specify at least 2 files to compare\n");
        return 1;
    }

    const char *file1 = argv[optind];
    png24_image image1 = {};
    int retval = read_image(file1, &image1);

    if (retval) {
        fprintf(stderr, "Can't read %s (%d)\n", file1, retval);
        return retval;
    }

    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));
    free(image1.row_pointers);
    free(image1.rgba_data);


    for (int arg = optind+1; arg < argc; arg++) {
        const char *file2 = argv[arg];

        png24_image image2 = {};
        retval = read_image(file2, &image2);
        if (retval) {
            fprintf(stderr, "Can't read %s (%d)\n", file2, retval);
            break;
        }

        if (image1.width != image2.width || image1.height != image2.height) {
            fprintf(stderr, "Image %s has different size than %s\n", file2, file1);
            retval = 4;
            break;
        }

        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);

        if (!modified) {
            fprintf(stderr, "Unable to process image %s\n", file2);
            retval = 4;
            break;
        }

        if (map_output_file) {
            dssim_set_save_ssim_maps(attr, 1, 1);
        }

        double dssim = dssim_compare(attr, original, modified);
        dssim_dealloc_image(modified);

        printf("%.8f\t%s\n", dssim, file2);


        if (map_output_file) {
            dssim_ssim_map map_meta = dssim_pop_ssim_map(attr, 0, 0);
            dssim_px_t *map = map_meta.data;
            dssim_rgba *out = (dssim_rgba*)map;
            for(int i=0; i < map_meta.width*map_meta.height; i++) {
                const dssim_px_t max = 1.0 - map[i];
                const dssim_px_t maxsq = max * max;
                out[i] = (dssim_rgba) {
                    .r = to_byte(max * 3.0),
                     .g = to_byte(maxsq * 6.0),
                      .b = to_byte(max / ((1.0 - map_meta.dssim) * 4.0)),
                       .a = 255,
                };
            }
            if (write_image(map_output_file, out, map_meta.width, map_meta.height)) {
                fprintf(stderr, "Can't write %s\n", map_output_file);
                free(map);
                return 1;
            }
            free(map);
        }
    }

    dssim_dealloc_image(original);
    dssim_dealloc_attr(attr);

    return retval;
}