コード例 #1
0
ファイル: weighting.c プロジェクト: tanghong668/imageflow
static struct flow_interpolation_line_contributions *
LineContributions_alloc(flow_c * context, const uint32_t line_length, const uint32_t windows_size)
{
    struct flow_interpolation_line_contributions * res = (struct flow_interpolation_line_contributions *)FLOW_malloc(
        context, sizeof(struct flow_interpolation_line_contributions));
    if (res == NULL) {
        FLOW_error(context, flow_status_Out_of_memory);
        return NULL;
    }
    res->WindowSize = windows_size;
    res->LineLength = line_length;
    res->ContribRow = (struct flow_interpolation_pixel_contributions *)FLOW_malloc(
        context, line_length * sizeof(struct flow_interpolation_pixel_contributions));
    if (!res->ContribRow) {
        FLOW_free(context, res);
        FLOW_error(context, flow_status_Out_of_memory);
        return NULL;
    }

    float * allWeights = FLOW_calloc_array(context, windows_size * line_length, float);
    if (!allWeights) {
        FLOW_free(context, res->ContribRow);
        FLOW_free(context, res);
        FLOW_error(context, flow_status_Out_of_memory);
        return NULL;
    }

    for (uint32_t i = 0; i < line_length; i++)
        res->ContribRow[i].Weights = allWeights + (i * windows_size);

    return res;
}
コード例 #2
0
ファイル: helpers_visual.c プロジェクト: lasote/imageflow
bool visual_compare_two(flow_c * c, struct flow_bitmap_bgra * a, struct flow_bitmap_bgra * b, const char * comparison_title,
                        double * out_dssim, bool save_bitmaps, bool generate_visual_diff, const char * file_,
                        const char * func_, int line_number, const char * storage_relative_to)
{

    char checksum_a[34];

    char checksum_b[34];
    // compute checksum of bitmap (two checksums, actually - one for configuration, another for bitmap bytes)
    if (!checksum_bitmap(c, a, checksum_a, 34)) {
        FLOW_error(c, flow_status_Invalid_argument);
        return false;
    }
    if (!checksum_bitmap(c, b, checksum_b, 34)) {
        FLOW_error(c, flow_status_Invalid_argument);
        return false;
    }

    // Compare
    if (strcmp(checksum_a, checksum_b) == 0) {
        if (memcmp(a->pixels, b->pixels, a->stride * a->h) == 0) {
            *out_dssim = 0;
            return true; // It matches!
        } else {
            // Checksum collsion
            exit(901);
        }
    }
    if (save_bitmaps) {
        // They differ
        if (!save_bitmap_to_visuals(c, a, checksum_a, "A", storage_relative_to)) {
            FLOW_error_return(c);
        }
        if (!save_bitmap_to_visuals(c, b, checksum_b, "B", storage_relative_to)) {
            FLOW_error_return(c);
        }
        // Diff the two, generate a third PNG. Also get PSNR metrics from imagemagick
        if (!diff_images(c, checksum_a, checksum_b, out_dssim, generate_visual_diff && save_bitmaps, storage_relative_to)) {
            FLOW_error_return(c);
        }
        // Dump to HTML=
        if (!append_html(c, comparison_title, checksum_a, checksum_b, storage_relative_to)) {
            FLOW_error_return(c);
        }
    }

    return false;
}
コード例 #3
0
ファイル: helpers_visual.c プロジェクト: lasote/imageflow
bool diff_image_pixels(flow_c * c, struct flow_bitmap_bgra * a, struct flow_bitmap_bgra * b, size_t * diff_count,
                       size_t * total_delta, size_t print_this_many_differences, size_t stop_counting_at)
{
    if (a->w != b->w || a->h != b->h || a->fmt != b->fmt || a->fmt != flow_bgra32) {
        FLOW_error(c, flow_status_Invalid_argument);
        return false;
    }
    *diff_count = 0;
    *total_delta = 0;
    for (size_t pixel_index = 0; pixel_index < a->h * a->stride; pixel_index++) {
        if (a->pixels[pixel_index] != b->pixels[pixel_index]) {
            int x = (pixel_index % (a->stride)) / 4;
            int y = pixel_index / a->stride;
            int channel = pixel_index % 4;
            const char channels[] = { "BGRA" };
            if (*diff_count < print_this_many_differences) {
                fprintf(stderr, " (%d,%d) %ca=%d %cb=%d ", x, y, channels[channel], a->pixels[pixel_index],
                        channels[channel], b->pixels[pixel_index]);
            }
            (*diff_count)++;
            (*total_delta) += abs(a->pixels[pixel_index] - b->pixels[pixel_index]);
            if (stop_counting_at == *diff_count)
                return true; // We stop comparing after X many differences
        }
    }
    return true;
}
コード例 #4
0
ファイル: helpers_visual.c プロジェクト: lasote/imageflow
static bool append_html(flow_c * c, const char * name, const char * checksum_a, const char * checksum_b,
                       const char * storage_relative_to)
{
    char filename[2048];
    if (!create_path_from_relative(c, storage_relative_to, true, filename, 2048, "/visuals/visuals.html")) {
        FLOW_add_to_callstack(c);
        return false;
    }
    static bool first_write = true;

    FILE * fp = fopen(filename, first_write ? "w" : "a");
    if (fp == NULL) {
        FLOW_error(c, flow_status_IO_error);
        return false;
    }
    if (first_write) {
        // Write the header here
    }
    if (checksum_b == NULL) {
        fprintf(fp, "<h1>%s</h2>\n<img class=\"current\" src=\"%s.png\"/>", name, checksum_a);
    } else {
        fprintf(fp, "<h1>%s</h2>\n<img class=\"current\" src=\"%s.png\"/><img class=\"correct\" src=\"%s.png\"/><img "
                    "class=\"diff\" src=\"compare_%s_vs_%s.png\"/>",
                name, checksum_a, checksum_b, checksum_a, checksum_b);
    }

    fclose(fp);
    first_write = false;
    return true;
}
コード例 #5
0
ファイル: job_graph.c プロジェクト: lasote/imageflow
bool flow_job_link_codecs(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref)
{

    if (graph_ref == NULL || *graph_ref == NULL) {
        FLOW_error(c, flow_status_Null_argument);
        return false;
    }
    if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
        FLOW_error_return(c);
    }

    struct flow_graph * g = *graph_ref;
    int32_t i;
    for (i = 0; i < g->next_node_id; i++) {
        if (g->nodes[i].type == flow_ntype_decoder || g->nodes[i].type == flow_ntype_encoder) {
            uint8_t * info_bytes = &g->info_bytes[g->nodes[i].info_byte_index];
            struct flow_nodeinfo_codec * info = (struct flow_nodeinfo_codec *)info_bytes;
            if (info->codec == NULL) {
                info->codec = flow_job_get_codec_instance(c, job, info->placeholder_id);

                if (info->codec == NULL)
                    FLOW_error_msg(c, flow_status_Graph_invalid,
                                   "No matching codec or io found for placeholder id %d (node #%d).",
                                   info->placeholder_id, i);
            }
        }
    }

    return true;
}
コード例 #6
0
ファイル: weighting.c プロジェクト: tanghong668/imageflow
struct flow_interpolation_details * flow_interpolation_details_create(flow_c * context)
{
    struct flow_interpolation_details * d = FLOW_calloc_array(context, 1, struct flow_interpolation_details);
    if (d == NULL) {
        FLOW_error(context, flow_status_Out_of_memory);
        return NULL;
    }
    d->blur = 1;
    d->window = 2;
    d->p1 = d->q1 = 0;
    d->p2 = d->q2 = d->p3 = d->q3 = d->q4 = 1;
    d->sharpen_percent_goal = 0;
    return d;
}
コード例 #7
0
ファイル: job_graph.c プロジェクト: lasote/imageflow
bool flow_graph_pre_optimize_flatten(flow_c * c, struct flow_graph ** graph_ref)
{
    if (*graph_ref == NULL) {
        FLOW_error(c, flow_status_Null_argument);
        return false;
    }
    bool re_walk;
    do {
        re_walk = false;
        if (!flow_graph_walk_dependency_wise(c, NULL, graph_ref, node_visitor_flatten, NULL, &re_walk)) {
            FLOW_error_return(c);
        }
    } while (re_walk);
    return true;
}
コード例 #8
0
ファイル: job_graph.c プロジェクト: lasote/imageflow
bool flow_graph_optimize(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref)
{
    if (*graph_ref == NULL) {
        FLOW_error(c, flow_status_Null_argument);
        return false;
    }
    bool re_walk;
    do {
        re_walk = false;
        if (!flow_graph_walk(c, job, graph_ref, node_visitor_optimize, NULL, &re_walk)) {
            FLOW_error_return(c);
        }
    } while (re_walk);
    return true;
}
コード例 #9
0
ファイル: helpers_visual.c プロジェクト: lasote/imageflow
bool append_checksum(flow_c * c, char checksum[34], const char * name, const char * storage_relative_to)
{
    char filename[2048];
    if (!create_path_from_relative(c, storage_relative_to, true, filename, 2048, "/visuals/checksums.list")) {
        FLOW_add_to_callstack(c);
        return false;
    }
    FILE * fp = fopen(filename, "a");
    if (fp == NULL) {
        FLOW_error(c, flow_status_IO_error);
        return false;
    }
    fprintf(fp, "%s\n%s\n", name, &checksum[0]);
    fclose(fp);
    return true;
}
コード例 #10
0
ファイル: job_graph.c プロジェクト: lasote/imageflow
bool flow_job_execute_where_certain(flow_c * c, struct flow_job * job, struct flow_graph ** g)
{
    if (*g == NULL) {
        FLOW_error(c, flow_status_Null_argument);
        return false;
    }

    //    //Resets and creates state tracking for this graph
    //    if (!flow_job_create_state(c,job, *g)){
    //        FLOW_error_return(c);
    //    }

    if (!flow_graph_walk_dependency_wise(c, job, g, node_visitor_execute, NULL, NULL)) {
        FLOW_error_return(c);
    }
    return true;
}
コード例 #11
0
ファイル: helpers_visual.c プロジェクト: lasote/imageflow
static bool load_checksums(flow_c * c, struct named_checksum ** checksums, size_t * checksum_count,
                          const char * storage_relative_to)
{
    static struct named_checksum * list = NULL;
    static size_t list_size = 0;

    if (list == NULL) {
        char filename[2048];
        if (!create_path_from_relative(c,storage_relative_to, false, filename, 2048, "/visuals/checksums.list")) {
            FLOW_add_to_callstack(c);
            return false;
        }
        //fprintf(stderr, "Using checkums from %s.", filename);
        FILE * fp;
        char * line_a = NULL;
        size_t len_a = 0;
        int64_t read_a;
        char * line_b = NULL;
        size_t len_b = 0;
        int64_t read_b;

        fp = fopen(filename, "r");
        if (fp == NULL) {
            FLOW_error(c, flow_status_IO_error);
            return false;
        }

        list_size = 200;
        list = (struct named_checksum *)calloc(list_size, sizeof(struct named_checksum));

        size_t index = 0;
        while (true) {
            // Read lines in pairs
            read_a = flow_getline(&line_a, &len_a, fp);
            if (read_a == -1) {
                break;
            }
            read_b = flow_getline(&line_b, &len_b, fp);
            if (read_b == -1) {
                free(line_a);
                break;
            }
            // Drop newlines if present
            if (line_a[read_a - 1] == '\n') {
                line_a[read_a - 1] = '\0';
            }
            if (line_b[read_b - 1] == '\n') {
                line_b[read_b - 1] = '\0';
            }
            // Save
            list[index].name = line_a;
            list[index].checksum = line_b;
            line_a = NULL;
            line_b = NULL;
            index++;
            if (index >= list_size) {
                FLOW_error_msg(c, flow_status_IO_error,
                               "Could not read in entire checksum file. Please increase list_size above %ul.",
                               list_size);
                fclose(fp);
                return false;
            }
        }
        list_size = index;
        fclose(fp);
    }
    *checksum_count = list_size;
    *checksums = list;

    return true;
}
コード例 #12
0
ファイル: helpers_visual.c プロジェクト: lasote/imageflow
bool visual_compare(flow_c * c, struct flow_bitmap_bgra * bitmap, const char * name, bool store_checksums,
                    size_t off_by_one_byte_differences_permitted, const char * file_, const char * func_,
                    int line_number, const char * storage_relative_to)
{

    char checksum[34];

    // compute checksum of bitmap (two checksums, actually - one for configuration, another for bitmap bytes)
    if (!checksum_bitmap(c, bitmap, checksum, 34)) {
        FLOW_error(c, flow_status_Invalid_argument);
        return false;
    }
    // Load stored checksum
    char * stored_checksum = get_checksum_for(c, name, storage_relative_to);

    // Compare
    if (stored_checksum != NULL && strcmp(checksum, stored_checksum) == 0) {
        // Make sure the file is created for later
        if (!save_bitmap_to_visuals(c, bitmap, checksum, "trusted", storage_relative_to)) {
            FLOW_error_return(c);
        }
        return true; // It matches!
    }

    if (stored_checksum == NULL) {
        // No stored checksum for this name
        if (store_checksums) {
            if (!append_checksum(c, checksum, name, storage_relative_to)) {
                FLOW_error_return(c);
            }
            fprintf(stderr, "===============\n%s\nStoring checksum %s, since FLOW_STORE_CHECKSUMS was set.\n ", name,
                    &checksum[0]);
        } else {
            fprintf(stderr, "===============\n%s\nThere is no stored checksum for this test; #define "
                            "FLOW_STORE_CHECKSUMS and rerun to set the initial value to %s.\n ",
                    name, &checksum[0]);
        }

        fprintf(stderr, "%s:%d in function %s\n", file_, line_number, func_);
    } else {
        fprintf(stderr, "===============\n%s\nThe stored checksum [%s] differs from the current result [%s]. Open "
                        "visuals/visuals.html to comapre.\n ",
                name, stored_checksum, checksum);
        fprintf(stderr, "%s:%d in function %s\n", file_, line_number, func_);
    }

    // The hash differs
    // Save ours so we can see it
    if (!save_bitmap_to_visuals(c, bitmap, checksum, "current", storage_relative_to)) {
        FLOW_error_return(c);
    }
    if (stored_checksum == NULL && store_checksums) {
        // Don't fail the test for an non-stored checksum. We may be trying to commit several new checksums
        return true;
    }

    if (stored_checksum != NULL) {
        // Try to download "old" png from S3 using the checksums as an address.
        if (!download_by_checksum(c, stored_checksum, storage_relative_to)) {
            FLOW_error_return(c);
        }

        // First try a byte-by-byte comparison to eliminate floating-point error
        struct flow_bitmap_bgra * old;
        if (!load_image(c, stored_checksum, &old, c, storage_relative_to)) {
            FLOW_error_return(c);
        }
        size_t differences;
        size_t differences_to_print = 0; // 100
        size_t total_delta;
        if (!diff_image_pixels(c, old, bitmap, &differences, &total_delta, 0,
                               off_by_one_byte_differences_permitted + 4096)) {
            FLOW_error_return(c);
        }
        // If the difference is a handful of off-by-one rounding changes, just print the different bytes.
        if (differences < off_by_one_byte_differences_permitted && total_delta == differences) {
            if (!diff_image_pixels(c, old, bitmap, &differences, &total_delta, differences_to_print,
                                   off_by_one_byte_differences_permitted + 4096)) {
                FLOW_error_return(c);
            }
            fprintf(stderr, "\nA total of %lu bytes (of %lu) differed between the bitmaps (off by one). Less than "
                            "failure threshold of %lu\n",
                    differences, (size_t)old->stride * (size_t)old->h, off_by_one_byte_differences_permitted);
            return true;
        } else {

            double dssim;
            // Diff the two, generate a third PNG. Also get PSNR metrics from imagemagick
            if (!diff_images(c, checksum, stored_checksum, &dssim, true, storage_relative_to)) {
                FLOW_error_return(c);
            }
            fprintf(stdout, "DSSIM %.20f between %s and %s\n", dssim, stored_checksum, checksum);

            // Dump to HTML=
            if (!append_html(c, name, checksum, stored_checksum, storage_relative_to)) {
                FLOW_error_return(c);
            }
        }
        flow_bitmap_bgra_destroy(c, old);
    }

    return false;
}
コード例 #13
0
ファイル: job_graph.c プロジェクト: lasote/imageflow
bool flow_job_execute(flow_c * c, struct flow_job * job, struct flow_graph ** graph_ref)
{
    if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
        FLOW_error_return(c);
    }
    if (!flow_job_link_codecs(c, job, graph_ref)) {
        FLOW_error_return(c);
    }

    // States for a node
    // New
    // OutboundDimensionsKnown
    // Flattened
    // Optimized
    // LockedForExecution
    // Executed
    int32_t passes = 0;
    while (!flow_job_graph_fully_executed(c, job, *graph_ref)) {
        if (passes >= job->max_calc_flatten_execute_passes) {
            FLOW_error(c, flow_status_Maximum_graph_passes_exceeded);
            return false;
        }
        if (!flow_job_populate_dimensions_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_graph_pre_optimize_flatten(c, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_populate_dimensions_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_graph_optimize(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_populate_dimensions_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_graph_post_optimize_flatten(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_populate_dimensions_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
        if (!flow_job_execute_where_certain(c, job, graph_ref)) {
            FLOW_error_return(c);
        }
        passes++;

        if (!flow_job_notify_graph_changed(c, job, *graph_ref)) {
            FLOW_error_return(c);
        }
    }
    if (job->next_graph_version > 0 && job->render_last_graph
        && !flow_job_render_graph_to_png(c, job, *graph_ref, job->next_graph_version - 1)) {
        FLOW_error_return(c);
    }
    return true;
}
コード例 #14
0
ファイル: weighting.c プロジェクト: tanghong668/imageflow
struct flow_interpolation_line_contributions *
flow_interpolation_line_contributions_create(flow_c * context, const uint32_t output_line_size,
                                             const uint32_t input_line_size,
                                             const struct flow_interpolation_details * details)
{
    const double sharpen_ratio = flow_interpolation_details_percent_negative_weight(details);
    const double desired_sharpen_ratio = details->sharpen_percent_goal / 100.0;

    const double scale_factor = (double)output_line_size / (double)input_line_size;
    const double downscale_factor = fmin(1.0, scale_factor);
    const double half_source_window = (details->window + 0.5) / downscale_factor;

    const uint32_t allocated_window_size = (int)ceil(2 * (half_source_window - TONY)) + 1;
    uint32_t u, ix;
    struct flow_interpolation_line_contributions * res
        = LineContributions_alloc(context, output_line_size, allocated_window_size);
    if (res == NULL) {
        FLOW_add_to_callstack(context);
        return NULL;
    }
    double negative_area = 0;
    double positive_area = 0;

    for (u = 0; u < output_line_size; u++) {
        const double center_src_pixel = ((double)u + 0.5) / scale_factor - 0.5;

        const int left_edge = (int)floor(center_src_pixel) - ((allocated_window_size - 1) / 2);
        const int right_edge = left_edge + allocated_window_size - 1;

        const uint32_t left_src_pixel = (uint32_t)int_max(0, left_edge);
        const uint32_t right_src_pixel = (uint32_t)int_min(right_edge, (int)input_line_size - 1);

        double total_weight = 0.0;
        double total_negative_weight = 0.0;

        const uint32_t source_pixel_count = right_src_pixel - left_src_pixel + 1;

        if (source_pixel_count > allocated_window_size) {
            flow_interpolation_line_contributions_destroy(context, res);
            FLOW_error(context, flow_status_Invalid_internal_state);
            return NULL;
        }

        res->ContribRow[u].Left = left_src_pixel;
        res->ContribRow[u].Right = right_src_pixel;

        float * weights = res->ContribRow[u].Weights;

        // commented: additional weight for edges (doesn't seem to be too effective)
        // for (ix = left_edge; ix <= right_edge; ix++) {
        for (ix = left_src_pixel; ix <= right_src_pixel; ix++) {
            int tx = ix - left_src_pixel;
            // int tx = min(max(ix, left_src_pixel), right_src_pixel) - left_src_pixel;
            double add = (*details->filter)(details, downscale_factor *((double)ix - center_src_pixel));
            if (fabs(add) <= 0.00000002){
                add = 0.0;
                // Weights below a certain threshold make consistent x-plat
                // integration test results impossible. pos/neg zero, etc.
                // They should be rounded down to zero at the threshold at which results are consistent.
            }
            weights[tx] = (float)add;
            total_weight += add;
            total_negative_weight -= fmin(0,add);
        }

        float neg_factor, pos_factor;
        if (total_weight <= 0 || desired_sharpen_ratio > sharpen_ratio){
            float total_positive_weight = total_weight + total_negative_weight;
            float target_negative_weight = desired_sharpen_ratio * total_positive_weight;
            pos_factor = 1;
            neg_factor = target_negative_weight / total_negative_weight;
        }else{
            neg_factor = pos_factor = (float)(1.0f / total_weight);
        }
        for (ix = 0; ix < source_pixel_count; ix++) {
            if (weights[ix] < 0) {
                weights[ix] *= neg_factor;
                negative_area -= weights[ix];
            } else {
                weights[ix] *= pos_factor;
                positive_area += weights[ix];
            }
        }

        //Shrink to improve perf & result consistency
        int32_t iix;
        //Shrink region from the right
        for (iix = source_pixel_count - 1; iix >= 0; iix--){
            if (weights[iix] != 0) break;
            res->ContribRow[u].Right--;
        }
        //Shrink region from the left
        for (iix = 0; iix < (int32_t)source_pixel_count; iix++){
            if (weights[0] != 0) break;
            res->ContribRow[u].Weights++;
            weights++;
            res->ContribRow[u].Left++;
        }
    }
    res->percent_negative = negative_area / positive_area;
    return res;
}