double EigenSolverPoissonImageEditing::solve(const NamedParameters& solverParameters, const NamedParameters& problemParameters, bool profileSolve, std::vector<SolverIteration>& iters)
{
    int numUnknowns = 0;
    std::unordered_map<vec2i, int, vec2iHash> pixelLocationsToIndex;
    std::vector<vec2i> pixelLocations;
    size_t pixelCount = m_dims[0] * m_dims[1];
    std::vector<float4> h_unknownFloat(pixelCount);
    std::vector<float4> h_target(pixelCount);
    std::vector<float>  h_mask(pixelCount);

    findAndCopyArrayToCPU("X", h_unknownFloat, problemParameters);
    findAndCopyArrayToCPU("T", h_target, problemParameters);
    findAndCopyArrayToCPU("M", h_mask, problemParameters);

    for (int y = 0; y < (int)m_dims[1]; ++y) {
        for (int x = 0; x < (int)m_dims[0]; ++x) {
            if (h_mask[y*m_dims[0] + x] == 0.0f) {
                ++numUnknowns;
                vec2i p(x, y);
                pixelLocationsToIndex[p] =(int)pixelLocations.size();
                pixelLocations.push_back(p);
            }
        }
    }
    printf("# Unknowns: %d\n", numUnknowns);
    int numResiduals = (int)pixelLocations.size() * 4;

    Eigen::VectorXf x_r(numUnknowns), b_r(numResiduals);
    Eigen::VectorXf x_g(numUnknowns), b_g(numResiduals);
    Eigen::VectorXf x_b(numUnknowns), b_b(numResiduals);
    Eigen::VectorXf x_a(numUnknowns), b_a(numResiduals);

    b_r.setZero();
    b_g.setZero();
    b_b.setZero();
    b_a.setZero();

    for (int i = 0; i < pixelLocations.size(); ++i) {
        vec2i p = pixelLocations[i];
        float4 color = sampleImage(h_unknownFloat.data(), p, m_dims[0]);
        x_r[i] = color.x;
        //printf("%f\n", color.x);
        x_g[i] = color.y;
        x_b[i] = color.z;
        x_a[i] = color.w;
    }
    SpMatrixf A(numResiduals, numUnknowns);
    A.setZero();
    printf("Constructing Matrix\n");
    std::vector<Tripf> entriesA;

    std::vector<vec2i> offsets;
    offsets.push_back(vec2i(-1, 0));
    offsets.push_back(vec2i(1, 0));
    offsets.push_back(vec2i(0, -1));
    offsets.push_back(vec2i(0, 1));

    for (int i = 0; i < pixelLocations.size(); ++i) {
        vec2i p = pixelLocations[i];
        int numInternalNeighbors = 0;
        float4 g_p = sampleImage(h_target.data(), p, m_dims[0]);
        int j = 0;

        for (vec2i off : offsets) {
            vec2i q = p + off;
            if (q.x >= 0 && q.y >= 0 && q.x < (int)m_dims[0] && q.y < (int)m_dims[1]) {
                auto it = pixelLocationsToIndex.find(q);
                int row = 4 * i + j;
                if (it == pixelLocationsToIndex.end()) {
                    float4 f_q = sampleImage(h_unknownFloat.data(), q, m_dims[0]);
                    b_r[row] += f_q.x;
                    b_g[row] += f_q.y;
                    b_b[row] += f_q.z;
                    b_a[row] += f_q.w;
                }
                else {
                    entriesA.push_back(Tripf(row, it->second, -1.0f));
                }
                entriesA.push_back(Tripf(row, i, 1.0f));

                float4 g_q = sampleImage(h_target.data(), q, m_dims[0]);
                b_r[row] += (g_p.x - g_q.x);
                b_g[row] += (g_p.y - g_q.y);
                b_b[row] += (g_p.z - g_q.z);
                b_a[row] += (g_p.w - g_q.w);
            }
            ++j;
            
        }
    }
    

    printf("Entries Set\n");
    A.setFromTriplets(entriesA.begin(), entriesA.end());
    printf("Sparse Matrix Constructed\n");
    A.makeCompressed();
    printf("Matrix Compressed\n");
    {
        float totalCost = 0.0f;
        
        float cost_r = (A*x_r - b_r).squaredNorm();
        float cost_g = (A*x_g - b_g).squaredNorm();
        float cost_b = (A*x_b - b_b).squaredNorm();
        float cost_a = (A*x_a - b_a).squaredNorm();
        totalCost = cost_r + cost_g + cost_b + cost_a;
        printf("Initial Cost: %f : (%f, %f, %f, %f)\n", totalCost, cost_r, cost_g, cost_b, cost_a);

    }
    

    AxEqBSolver solver;
    solver.setMaxIterations(97);
    printf("Solvers Initialized\n");

    clock_t start = clock(), diff;
    
    solver.compute(A);
    //printf("solver.compute(A)\n");
    solveAxEqb(solver, b_r, x_r);
    //printf("Red solve done\n");
    solveAxEqb(solver, b_g, x_g);
    //printf("Green solve done\n");
    solveAxEqb(solver, b_b, x_b);
    //printf("Blue solve done\n");
    solveAxEqb(solver, b_a, x_a);

    diff = clock() - start;
    printf("Time taken %f ms\n", diff*1000.0 / double(CLOCKS_PER_SEC));

    float totalCost = 0.0f;
 
    float cost_r = (A*x_r - b_r).squaredNorm(); 
    float cost_g = (A*x_g - b_g).squaredNorm();
    float cost_b = (A*x_b - b_b).squaredNorm();
    float cost_a = (A*x_a - b_a).squaredNorm();
    totalCost = cost_r + cost_g + cost_b + cost_a;
    printf("Final Cost: %f : (%f, %f, %f, %f)\n", totalCost, cost_r, cost_g, cost_b, cost_a);

    for (int i = 0; i < pixelLocations.size(); ++i) {
        setPixel(h_unknownFloat.data(), pixelLocations[i], m_dims[0], x_r[i], x_g[i], x_b[i]);
    }
    findAndCopyToArrayFromCPU("X", h_unknownFloat, problemParameters);;
    return (double)totalCost;

}
Esempio n. 2
0
bool yee_compare(CompareArgs &args)
{
    if ((args.image_a_->get_width()  != args.image_b_->get_width()) or
        (args.image_a_->get_height() != args.image_b_->get_height()))
    {
        args.error_string_ = "Image dimensions do not match\n";
        return false;
    }

    const auto w = args.image_a_->get_width();
    const auto h = args.image_a_->get_height();
    const auto dim = w * h;

    auto identical = true;
    for (auto i = 0u; i < dim; i++)
    {
        if (args.image_a_->get(i) != args.image_b_->get(i))
        {
            identical = false;
            break;
        }
    }
    if (identical)
    {
        args.error_string_ = "Images are binary identical\n";
        return true;
    }

    // Assuming colorspaces are in Adobe RGB (1998) convert to XYZ.
    std::vector<float> a_lum(dim);
    std::vector<float> b_lum(dim);

    std::vector<float> a_a(dim);
    std::vector<float> b_a(dim);
    std::vector<float> a_b(dim);
    std::vector<float> b_b(dim);

    if (args.verbose_)
    {
        std::cout << "Converting RGB to XYZ\n";
    }

    const auto gamma = args.gamma_;
    const auto luminance = args.luminance_;

    #pragma omp parallel for shared(args, a_lum, b_lum, a_a, a_b, b_a, b_b)
    for (auto y = 0; y < static_cast<ptrdiff_t>(h); y++)
    {
        for (auto x = 0u; x < w; x++)
        {
            const auto i = x + y * w;
            const auto a_color_r = powf(args.image_a_->get_red(i) / 255.0f,
                                        gamma);
            const auto a_color_g = powf(args.image_a_->get_green(i) / 255.0f,
                                        gamma);
            const auto a_color_b = powf(args.image_a_->get_blue(i) / 255.0f,
                                        gamma);
            float a_x;
            float a_y;
            float a_z;
            adobe_rgb_to_xyz(a_color_r, a_color_g, a_color_b, a_x, a_y, a_z);
            float l;
            xyz_to_lab(a_x, a_y, a_z, l, a_a[i], a_b[i]);
            const auto b_color_r = powf(args.image_b_->get_red(i) / 255.0f,
                                        gamma);
            const auto b_color_g = powf(args.image_b_->get_green(i) / 255.0f,
                                        gamma);
            const auto b_color_b = powf(args.image_b_->get_blue(i) / 255.0f,
                                        gamma);
            float b_x;
            float b_y;
            float b_z;
            adobe_rgb_to_xyz(b_color_r, b_color_g, b_color_b, b_x, b_y, b_z);
            xyz_to_lab(b_x, b_y, b_z, l, b_a[i], b_b[i]);
            a_lum[i] = a_y * luminance;
            b_lum[i] = b_y * luminance;
        }
    }

    if (args.verbose_)
    {
        std::cout << "Constructing Laplacian Pyramids\n";
    }

    const LPyramid la(a_lum, w, h);
    const LPyramid lb(b_lum, w, h);

    const auto num_one_degree_pixels =
        to_degrees(2 *
                   std::tan(args.field_of_view_ * to_radians(.5f)));
    const auto pixels_per_degree = w / num_one_degree_pixels;

    if (args.verbose_)
    {
        std::cout << "Performing test\n";
    }

    const auto adaptation_level = adaptation(num_one_degree_pixels);

    float cpd[MAX_PYR_LEVELS];
    cpd[0] = 0.5f * pixels_per_degree;
    for (auto i = 1u; i < MAX_PYR_LEVELS; i++)
    {
        cpd[i] = 0.5f * cpd[i - 1];
    }
    const auto csf_max = csf(3.248f, 100.0f);

    static_assert(MAX_PYR_LEVELS > 2,
                  "MAX_PYR_LEVELS must be greater than 2");

    float f_freq[MAX_PYR_LEVELS - 2];
    for (auto i = 0u; i < MAX_PYR_LEVELS - 2; i++)
    {
        f_freq[i] = csf_max / csf(cpd[i], 100.0f);
    }

    auto pixels_failed = 0u;
    auto error_sum = 0.;

    #pragma omp parallel for reduction(+ : pixels_failed, error_sum) \
        shared(args, a_a, a_b, b_a, b_b, cpd, f_freq)
    for (auto y = 0; y < static_cast<ptrdiff_t>(h); y++)
    {
        for (auto x = 0u; x < w; x++)
        {
            const auto index = y * w + x;
            const auto adapt = std::max((la.get_value(x, y, adaptation_level) +
                                         lb.get_value(x, y, adaptation_level)) * 0.5f,
                                        1e-5f);
            auto sum_contrast = 0.f;
            auto factor = 0.f;
            for (auto i = 0u; i < MAX_PYR_LEVELS - 2; i++)
            {
                const auto n1 =
                    fabsf(la.get_value(x, y, i) - la.get_value(x, y, i + 1));
                const auto n2 =
                    fabsf(lb.get_value(x, y, i) - lb.get_value(x, y, i + 1));
                const auto numerator = std::max(n1, n2);
                const auto d1 = fabsf(la.get_value(x, y, i + 2));
                const auto d2 = fabsf(lb.get_value(x, y, i + 2));
                const auto denominator = std::max(std::max(d1, d2), 1e-5f);
                const auto contrast = numerator / denominator;
                const auto f_mask = mask(contrast * csf(cpd[i], adapt));
                factor += contrast * f_freq[i] * f_mask;
                sum_contrast += contrast;
            }
            sum_contrast = std::max(sum_contrast, 1e-5f);
            factor /= sum_contrast;
            factor = std::min(std::max(factor, 1.f), 10.f);
            const auto delta =
                fabsf(la.get_value(x, y, 0) - lb.get_value(x, y, 0));
            error_sum += delta;
            auto pass = true;

            // pure luminance test
            if (delta > factor * tvi(adapt))
            {
                pass = false;
            }

            if (not args.luminance_only_)
            {
                // CIE delta E test with modifications
                auto color_scale = args.color_factor_;
                // ramp down the color test in scotopic regions
                if (adapt < 10.0f)
                {
                    // Don't do color test at all.
                    color_scale = 0.0;
                }
                const auto da = a_a[index] - b_a[index];
                const auto db = a_b[index] - b_b[index];
                const auto delta_e = (da * da + db * db) * color_scale;
                error_sum += delta_e;
                if (delta_e > factor)
                {
                    pass = false;
                }
            }

            if (not pass)
            {
                pixels_failed++;
                if (args.image_difference_)
                {
                    args.image_difference_->set(255, 0, 0, 255, index);
                }
            }
            else
            {
                if (args.image_difference_)
                {
                    args.image_difference_->set(0, 0, 0, 255, index);
                }
            }
        }
    }

    const auto error_sum_buff =
        std::to_string(error_sum) + " error sum\n";

    const auto different =
        std::to_string(pixels_failed) + " pixels are different\n";

    // Always output image difference if requested.
    if (args.image_difference_)
    {
        args.image_difference_->write_to_file(args.image_difference_->get_name());

        args.error_string_ += "Wrote difference image to ";
        args.error_string_ += args.image_difference_->get_name();
        args.error_string_ += "\n";
    }

    if (pixels_failed < args.threshold_pixels_)
    {
        args.error_string_ = "Images are perceptually indistinguishable\n";
        args.error_string_ += different;
        return true;
    }

    args.error_string_ = "Images are visibly different\n";
    args.error_string_ += different;
    if (args.sum_errors_)
    {
        args.error_string_ += error_sum_buff;
    }

    return false;
}