【开发日志】2023.04 ZENO----Image Processing----CompositeCV、Composite2、Composite3

文章深入探讨了ZENO框架下的图像合成技术CompositeCV,展示了如何利用OpenCV进行图像的加减乘除等复合操作,以及使用掩码控制透明度和合成模式,如叠加、下层、内部等,适用于高级图像处理场景。
摘要由CSDN通过智能技术生成

(6条消息) 【开发日志】2023.04 ZENO----Composite----Combine composite123_EndlessDaydream的博客-CSDN博客icon-default.png?t=N3I4https://blog.csdn.net/Angelloveyatou/article/details/130190085CompositeCV

Input:

{"Foreground"},
{"Background"},
{"enum Add Subtract Multiply Divide", "mode", "Add"},
{"float", "Alpha1", "1"},
{"float", "Alpha2", "1"},

Output:

Add

Subtract

Multiply

Divide

struct CompositeCV: INode {
    virtual void apply() override {
        auto image1 = get_input<PrimitiveObject>("Foreground");
        auto image2 = get_input<PrimitiveObject>("Background");
        auto mode = get_input2<std::string>("mode");
        auto Alpha1 = get_input2<float>("Alpha1");
        auto Alpha2 = get_input2<float>("Alpha2");
        auto &a1 = image1->verts.attr<float>("alpha");
        auto &a2 = image2->verts.attr<float>("alpha");

        auto &ud1 = image1->userData();
        int w1 = ud1.get2<int>("w");
        int h1 = ud1.get2<int>("h");
        auto &ud2 = image2->userData();
        int w2 = ud2.get2<int>("w");
        int h2 = ud2.get2<int>("h");

        cv::Mat imagecvin1(h1, w1, CV_32FC3);
        cv::Mat imagecvin2(h2, w2, CV_32FC3);
        cv::Mat imagecvadd(h1, w1, CV_32FC3);
        cv::Mat imagecvsub(h1, w1, CV_32FC3);
        cv::Mat imagecvout(h1, w1, CV_32FC3);

        for (int i = 0; i < h1; i++) {
            for (int j = 0; j < w1; j++) {
                vec3f rgb = image1->verts[i * w1 + j];
                imagecvin1.at<cv::Vec3f>(i, j) = {rgb[0], rgb[1], rgb[2]};
            }
        }
        for (int i = 0; i < h2; i++) {
            for (int j = 0; j < w2; j++) {
                vec3f rgb = image2->verts[i * w2 + j];
                imagecvin2.at<cv::Vec3f>(i, j) = {rgb[0], rgb[1], rgb[2]};
            }
        }
        cv::resize(imagecvin2, imagecvin2,imagecvin1.size());
        if(mode == "Add"){
            cv::addWeighted(imagecvin1, Alpha1, imagecvin2, Alpha2, 0, imagecvout);
        }
        if(mode == "Subtract"){
            cv::subtract(imagecvin1*Alpha1, imagecvin2*Alpha2, imagecvout);
        }
        if(mode == "Multiply"){
            cv::multiply(imagecvin1*Alpha1, imagecvin2*Alpha2, imagecvout);
        }
        if(mode == "Divide"){
            cv::divide(imagecvin1*Alpha1, imagecvin2*Alpha2, imagecvout, 1,  -1);
        }
        if(mode == "Diff"){
            cv::absdiff(imagecvin1*Alpha1, imagecvin2*Alpha2, imagecvout);
        }

        for (int i = 0; i < h1; i++) {
            for (int j = 0; j < w1; j++) {
                cv::Vec3f rgb = imagecvout.at<cv::Vec3f>(i, j);
                image1->verts[i * w1 + j] = {rgb[0], rgb[1], rgb[2]};
            }
        }
        set_output("image", image1);
    }
};

ZENDEFNODE(CompositeCV, {
    {
        {"Foreground"},
        {"Background"},
        {"enum Add Subtract Multiply Divide Diff", "mode", "Add"},
        {"float", "Alpha1", "1"},
        {"float", "Alpha2", "1"},
    },
    {
        {"image"}
    },
    {},
    { "comp" },
});

Composite2

Input:

{"Foreground"},
{"Background"},

{"enum Over Under Atop Inside Outside Add Subtract Multiply Divide Diff Min Max Average Xor Alpha ", "compmode", "Over"}

Output:

Over

Under

Atop

Inside

Outside

Screen

Add

Subtract

Multiply

Divide

Diff

Min

Max

Average

Xor

Alpha

struct Composite2: INode {
    virtual void apply() override {
        auto image1 = get_input<PrimitiveObject>("Foreground");
        auto image2 = get_input<PrimitiveObject>("Background");
        auto compmode = get_input2<std::string>("compmode");
        auto &alpha1 = image1->verts.attr<float>("alpha");
        auto &alpha2 = image2->verts.attr<float>("alpha");
        auto &ud1 = image1->userData();
        int w1 = ud1.get2<int>("w");
        int h1 = ud1.get2<int>("h");
        auto &ud2 = image2->userData();
        int w2 = ud2.get2<int>("w");
        int h2 = ud2.get2<int>("h");
        if(compmode == "Over"){
            if (image1->verts.has_attr("alpha")) {
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb1 * l1 + rgb2 * (l2 - ((l1 != 0) && (l2 != 0) ? l2 : 0));
                    }
                }
            }
        }
        if(compmode == "Under"){
            if (image2->verts.has_attr("alpha")) {
                auto &alpha = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb2 * l2 + rgb1 * (l1 - ((l1 != 0) && (l2 != 0) ? l1 : 0));
                    }
                }
            }
        }
        if(compmode == "Atop"){
            if (image2->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb1 * ((l1 != 0) && (l2 != 0) ? l1 : 0) + rgb2 * ((l1 == 0) && (l2 != 0) ? l2 : 0);
                    }
                }
            }
        }
        if(compmode == "Inside"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb1 * ((l1 != 0) && (l2 != 0) ? l1 : 0);
                    }
                }
            }
        }
        if(compmode == "Outside"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb1 * ((l1 != 0) && (l2 == 0) ? l1 : 0);
                    }
                }
            }
        }
        if(compmode == "Add"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb1 * l1 + rgb2 * l2;
                    }
                }
            }
        }
        if(compmode == "Subtract"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb1 * l1 - rgb2 * l2;
                    }
                }
            }
        }
        if(compmode == "Multiply"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb1 * l1 * rgb2 * l2;
                    }
                }
            }
        }
        if(compmode == "Divide"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb1 * l1 / (rgb2 * l2);
                    }
                }
            }
        }
        if(compmode == "Diff"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = abs(rgb1*l1 - rgb2 * l2);
                    }
                }
            }
        }
        if(compmode == "Min"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = l1 <= l2 ? rgb1 * l1 : rgb2 * l2;
                    }
                }
            }
        }
        if(compmode == "Max"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = l1 >= l2 ? rgb1 * l1 : rgb2 * l2;
                    }
                }
            }
        }
        if(compmode == "Average"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        vec3f rgb3 = (rgb1+rgb2)/2;
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb3 * (l1+l2);
                    }
                }
            }
        }
        if(compmode == "Xor"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        vec3f rgb3 = {0, 0, 0};
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = (((l1 != 0) && (l2 != 0)) ? rgb3 : rgb1 * l1 + rgb2 * l2);
                    }
                }
            }
        }
        if(compmode == "Alpha"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        vec3f rgb3 = {1,1,1};
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb3 * ((l1 != 0) || (l2 != 0) ? 1 : 0);
                    }
                }
            }
        }
        if(compmode == "Screen"){
            if (image1->verts.has_attr("alpha")) {
                auto &alpha1 = image1->verts.attr<float>("alpha");
                auto &alpha2 = image2->verts.attr<float>("alpha");
                for (int i = 0; i < h1; i++) {
                    for (int j = 0; j < w1; j++) {
                        vec3f rgb1 = image1->verts[i * w1 + j];
                        vec3f rgb2 = image2->verts[i * w1 + j];
                        float var = (image1->verts[i * w1 + j][0]+image1->verts[i * w1 + j][1]+image1->verts[i * w1 + j][2])/3;
                        float l1 = alpha1[i * w1 + j];
                        float l2 = alpha2[i * w1 + j];
                        image1->verts[i * w1 + j] = rgb2 * l2 + rgb2 *((l1!=0 && l2!=0)? var: 0);
                    }
                }
            }
        }
        set_output("image", image1);
    }
};

ZENDEFNODE(Composite2, {
    {
        {"Foreground"},
        {"Background"},
        {"enum Over Under Atop Inside Outside Screen Add Subtract Multiply Divide Diff Min Max Average Xor Alpha ", "compmode", "Over"},
    },
    {
        {"image"}
    },
    {},
    { "comp" },
});

Composite3

Input:

    {"Foreground"},
    {"Background"},
    {"Mask1"},
    {"Mask2"},
    

{"enum Over Under Atop Inside Outside Add Subtract Multiply Divide Diff Min Max Average Xor Alpha", "compmode", "Over"} 

Output:

Add

Subtract

Multiply

Divide

Diff

Over

Under

Atop

Inside

Outside

Screen

Min

Max

Average

Xor

Alpha

struct Composite3: INode {
    virtual void apply() override {
        auto image1 = get_input<PrimitiveObject>("Foreground");
        auto image2 = get_input<PrimitiveObject>("Background");
        auto Mask1 = get_input<PrimitiveObject>("Mask1");
        auto Mask2 = get_input<PrimitiveObject>("Mask2");
        auto compmode = get_input2<std::string>("compmode");
        auto &ud1 = image1->userData();
        int w1 = ud1.get2<int>("w");
        int h1 = ud1.get2<int>("h");

        if (compmode == "Add") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb1 * l1 + rgb2 * l2;
                }
            }
        }
        if (compmode == "Subtract") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb1 * l1 - rgb2 * l2;
                }
            }
        }
        if (compmode == "Multiply") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb1 * l1 * rgb2 * l2;
                }
            }
        }
        if (compmode == "Divide") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb1 * l1 / (rgb2 * l2);
                }
            }
        }
        if (compmode == "Diff") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = abs(rgb1 * l1 - (rgb2 * l2));
                }
            }
        }
        if (compmode == "Over") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb1 * l1 + rgb2 * (l2 - ((l1 != 0) && (l2 != 0) ? l2 : 0));
                }
            }
        }
        if (compmode == "Under") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb2 * l2 + rgb1 * (l1 - ((l1 != 0) && (l2 != 0) ? l1 : 0));
                }
            }
        }
        if (compmode == "Atop") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] =
                            rgb1 * ((l1 != 0) && (l2 != 0) ? l1 : 0) + rgb2 * ((l1 == 0) && (l2 != 0) ? l2 : 0);
                }
            }
        }
        if (compmode == "Inside") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb1 * ((l1 != 0) && (l2 != 0) ? l1 : 0);
                }
            }
        }
        if (compmode == "Outside") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb1 * ((l1 != 0) && (l2 == 0) ? l1 : 0);
                }
            }
        }
        if (compmode == "Min") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = l1 <= l2 ? rgb1 * l1 : rgb2 * l2;
                }
            }
        }
        if (compmode == "Max") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = l1 >= l2 ? rgb1 * l1 : rgb2 * l2;
                }
            }
        }
        if (compmode == "Xor") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    vec3f rgb3 = {0, 0, 0};
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = (((l1 != 0) && (l2 != 0)) ? rgb3 : rgb1 * l1 + rgb2 * l2);
                }
            }
        }
        if (compmode == "Alpha") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    vec3f rgb3 = {1,1,1};
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb3 * ((l1 != 0) || (l2 != 0) ? 1 : 0);
                }
            }
        }
        if (compmode == "Average") {
            for (int i = 0; i < h1; i++) {
                for (int j = 0; j < w1; j++) {
                    vec3f rgb1 = image1->verts[i * w1 + j];
                    vec3f rgb2 = image2->verts[i * w1 + j];
                    vec3f rgb3 = (rgb1+rgb2)/2;
                    float l1 = Mask1->verts[i * w1 + j][0];
                    float l2 = Mask2->verts[i * w1 + j][0];
                    image1->verts[i * w1 + j] = rgb3 * (l1+l2);
                }
            }
        }
        set_output("image", image1);
    }
};

ZENDEFNODE(Composite3, {
    {
        {"Foreground"},
        {"Background"},
        {"Mask1"},
        {"Mask2"},
        {"enum Over Under Atop Inside Outside Add Subtract Multiply Divide Diff Min Max Average Xor Alpha", "compmode", "Over"},
    },
    {
        {"image"}
    },
    {},
    { "comp" },
});

UVProjectFromPlane.cpp 

#include <zeno/zeno.h>
#include <zeno/types/PrimitiveObject.h>
#include <zeno/types/NumericObject.h>
#include <zeno/types/HeatmapObject.h>
#include <zeno/types/UserData.h>
#include <zeno/utils/scope_exit.h>
#include <stdexcept>
#include <cstring>
#include <zeno/utils/log.h>
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#include <tinygltf/stb_image.h>

#define TINYEXR_IMPLEMENTATION
#include "tinyexr.h"
#include "zeno/utils/string.h"

static const float eps = 0.0001f;

namespace zeno {
struct UVProjectFromPlane : zeno::INode {
    virtual void apply() override {
        auto prim = get_input<PrimitiveObject>("prim");
        auto &uv = prim->verts.add_attr<vec3f>("uv");
        auto refPlane = get_input<PrimitiveObject>("refPlane");
        if (refPlane->verts.size() != 4) {
            zeno::log_error("refPlane must be 1 * 1 plane!");
            throw zeno::makeError("refPlane must be 1 * 1 plane!");
        }
        auto originPos = refPlane->verts[2];
        auto xOffset = refPlane->verts[0];
        auto yOffset = refPlane->verts[3];
//        zeno::log_info("xOffset:{}, originPos: {}", xOffset, originPos);
        auto uDir = zeno::normalize(xOffset - originPos);
        auto vDir = zeno::normalize(yOffset - originPos);
        auto uLength = zeno::length(xOffset - originPos);
        auto vLength = zeno::length(yOffset - originPos);
//        zeno::log_info("uDir:{], uLength: {}, n: {}", uDir, uLength);
        for (auto i = 0; i < prim->size(); i++) {
            auto &vert = prim->verts[i];
            auto offset = vert - originPos;
            auto proj = offset;
            auto u = zeno::clamp(zeno::dot(proj, uDir) / uLength, 0, 1);
            auto v = zeno::clamp(zeno::dot(proj, vDir) / vLength, 0, 1);
            uv[i] = zeno::vec3f(u,  v, 0);
        }
        auto &uv0 = prim->tris.add_attr<vec3f>("uv0");
        auto &uv1 = prim->tris.add_attr<vec3f>("uv1");
        auto &uv2 = prim->tris.add_attr<vec3f>("uv2");
        for (auto i = 0; i < prim->tris.size(); i++) {
            auto tri = prim->tris[i];
            uv0[i] = uv[tri[0]];
            uv1[i] = uv[tri[1]];
            uv2[i] = uv[tri[2]];
        }

        if(prim->loops.size()){
            prim->loops.add_attr<int>("uvs");
            for (auto i = 0; i < prim->loops.size(); i++) {
                auto lo = prim->loops[i];
                prim->loops.attr<int>("uvs")[i] = lo;
            }
            prim->uvs.resize(prim->size());
            for (auto i = 0; i < prim->size(); i++) {
                prim->uvs[i] = {uv[i][0], uv[i][1]};
            }
        }
        set_output("outPrim", std::move(prim));
    }
};

ZENDEFNODE(UVProjectFromPlane, {
    {
        {"PrimitiveObject", "prim"},
        {"PrimitiveObject", "refPlane"},
    },
    {
        {"PrimitiveObject", "outPrim"}
    },
    {},
    {"primitive"},
});

static zeno::vec2i uvRepeat(vec3f uv, int w, int h) {
    int iu = int(uv[0] * (w-eps)) % w;
    if (iu < 0) {
        iu += w;
    }
    int iv = int(uv[1] * (h-eps)) % h;
    if (iv < 0) {
        iv += h;
    }
    return {iu, iv};
}
static zeno::vec2i uvClampToEdge(vec3f uv, int w, int h) {
    int iu = clamp(int(uv[0] * (w-eps)), 0, (w-1));
    int iv = clamp(int(uv[1] * (h-eps)), 0, (h-1));
    return {iu, iv};
}

//static zeno::vec3f queryColorInner(vec2i uv, const uint8_t* data, int w, int n) {
    //int iu = uv[0];
    //int iv = uv[1];
    //int start = (iu + iv * w) * n;
    //float r = float(data[start]) / 255.0f;
    //float g = float(data[start+1]) / 255.0f;
    //float b = float(data[start+2]) / 255.0f;
    //return {r, g, b};
//}
static zeno::vec3f queryColorInner(vec2i uv, const float* data, int w, int n) {
    int iu = uv[0];
    int iv = uv[1];
    int start = (iu + iv * w) * n;
    float r = (data[start]);
    float g = (data[start+1]);
    float b = (data[start+2]);
    return {r, g, b};
}
void primSampleTexture(
    std::shared_ptr<PrimitiveObject> prim,
    const std::string &srcChannel,
    const std::string &dstChannel,
    std::shared_ptr<PrimitiveObject> img,
    const std::string &wrap,
    // ZHOUHANG: please add arg filter, which is enum NEAREST LINEAR, impl bilerp version for LINEAR
    vec3f borderColor,
    float remapMin,
    float remapMax
) {
    if (!img->userData().has("isImage")) throw zeno::Exception("not an image");
    using ColorT = float;
    const ColorT *data = (float *)img->verts.data();
    auto &clr = prim->add_attr<zeno::vec3f>(dstChannel);
    auto &uv = prim->attr<zeno::vec3f>(srcChannel);
    auto w = img->userData().get2<int>("w");
    auto h = img->userData().get2<int>("h");
    std::function<zeno::vec3f(vec3f, const ColorT*, int, int, int, vec3f)> queryColor;
    // if (filter == "NEAREST") {
    if (wrap == "REPEAT") {
        queryColor = [=] (vec3f uv, const ColorT* data, int w, int h, int n, vec3f _clr)-> vec3f {
            uv = (uv - remapMin) / (remapMax - remapMin);
            auto iuv = uvRepeat(uv, w, h);
            return queryColorInner(iuv, data, w, n);
        };
    }
    else if (wrap == "CLAMP_TO_EDGE") {
        queryColor = [=] (vec3f uv, const ColorT* data, int w, int h, int n, vec3f _clr)-> vec3f {
            uv = (uv - remapMin) / (remapMax - remapMin);
            auto iuv = uvClampToEdge(uv, w, h);
            return queryColorInner(iuv, data, w, n);
        };
    }
    else if (wrap == "CLAMP_TO_BORDER") {
        queryColor = [=] (vec3f uv, const ColorT* data, int w, int h, int n, vec3f clr)-> vec3f {
            uv = (uv - remapMin) / (remapMax - remapMin);
            if (uv[0] < 0 || uv[0] > 1 || uv[1] < 0 || uv[1] > 1) {
                return clr;
            }
            auto iuv = uvClampToEdge(uv, w, h);
            return queryColorInner(iuv, data, w, n);
        };
    }
    else {
        zeno::log_error("wrap type error");
        throw std::runtime_error("wrap type error");
    }
    // else if (filter == "NEAREST") {
    // copy-paste all above wrap ifelses
    // }

    #pragma omp parallel for
    for (auto i = 0; i < uv.size(); i++) {
        clr[i] = queryColor(uv[i], data, w, h, 3, borderColor);
    }
}

struct PrimSample2D : zeno::INode {
    virtual void apply() override {
        auto prim = get_input<PrimitiveObject>("prim");
        auto srcChannel = get_input2<std::string>("uvChannel");
        auto dstChannel = get_input2<std::string>("targetChannel");
        auto image = get_input2<PrimitiveObject>("image");
        auto wrap = get_input2<std::string>("wrap");
        auto borderColor = get_input2<vec3f>("borderColor");
        auto remapMin = get_input2<float>("remapMin");
        auto remapMax = get_input2<float>("remapMax");
        primSampleTexture(prim, srcChannel, dstChannel, image, wrap, borderColor, remapMin, remapMax);

        set_output("outPrim", std::move(prim));
    }
};
ZENDEFNODE(PrimSample2D, {
    {
        {"PrimitiveObject", "prim"},
        {"PrimitiveObject", "image"},
        {"string", "uvChannel", "uv"},
        {"string", "targetChannel", "clr"},
        {"float", "remapMin", "0"},
        {"float", "remapMax", "1"},
        {"enum REPEAT CLAMP_TO_EDGE CLAMP_TO_BORDER", "wrap", "REPEAT"},
        {"vec3f", "borderColor", "0,0,0"},
    },
    {
        {"PrimitiveObject", "outPrim"}
    },
    {},
    {"primitive"},
});
std::shared_ptr<PrimitiveObject> readImageFile(std::string const &path) {
    int w, h, n;
    stbi_set_flip_vertically_on_load(true);
    float* data = stbi_loadf(path.c_str(), &w, &h, &n, 0);
    if (!data) {
        throw zeno::Exception("cannot open image file at path: " + path);
    }
    scope_exit delData = [=] { stbi_image_free(data); };
    auto img = std::make_shared<PrimitiveObject>();
    img->verts.resize(w * h);
    if (n == 3) {
        std::memcpy(img->verts.data(), data, w * h * n * sizeof(float));
    } else if (n == 4) {
        auto &alpha = img->verts.add_attr<float>("alpha");
        for (int i = 0; i < w * h; i++) {
            img->verts[i] = {data[i*4+0], data[i*4+1], data[i*4+2]};
            alpha[i] = data[i*4+3];
        }
    } else if (n == 2) {
        for (int i = 0; i < w * h; i++) {
            img->verts[i] = {data[i*2+0], data[i*2+1], 0};
        }
    } else if (n == 1) {
        for (int i = 0; i < w * h; i++) {
            img->verts[i] = vec3f(data[i*2+0]);
        }
    } else {
        throw zeno::Exception("too much number of channels");
    }
    img->userData().set2("isImage", 1);
    img->userData().set2("w", w);
    img->userData().set2("h", h);
    return img;
}

std::shared_ptr<PrimitiveObject> readExrFile(std::string const &path) {
    int nx, ny, nc = 4;
    float* rgba;
    const char* err;
    int ret = LoadEXR(&rgba, &nx, &ny, path.c_str(), &err);
    if (ret != 0) {
        zeno::log_error("load exr: {}", err);
        throw std::runtime_error(zeno::format("load exr: {}", err));
    }
    nx = std::max(nx, 1);
    ny = std::max(ny, 1);
//    for (auto i = 0; i < ny / 2; i++) {
//        for (auto x = 0; x < nx * 4; x++) {
//            auto index1 = i * (nx * 4) + x;
//            auto index2 = (ny - 1 - i) * (nx * 4) + x;
//            std::swap(rgba[index1], rgba[index2]);
//        }
//    }

    auto img = std::make_shared<PrimitiveObject>();
    img->verts.resize(nx * ny);

    auto &alpha = img->verts.add_attr<float>("alpha");
    for (int i = 0; i < nx * ny; i++) {
        img->verts[i] = {rgba[i*4+0], rgba[i*4+1], rgba[i*4+2]};
        alpha[i] = rgba[i*4+3];
    }
//
    img->userData().set2("isImage", 1);
    img->userData().set2("w", nx);
    img->userData().set2("h", ny);
    return img;
}

struct ReadImageFile : INode {
    virtual void apply() override {
        auto path = get_input2<std::string>("path");
        if (zeno::ends_with(path, ".exr", false)) {
            set_output("image", readExrFile(path));
        }
        else {
            set_output("image", readImageFile(path));
        }
    }
};
ZENDEFNODE(ReadImageFile, {
    {
        {"readpath", "path"},
    },
    {
        {"PrimitiveObject", "image"},
    },
    {},
    {"comp"},
});
}

Imgcv.cpp (小彭老师)

#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <zeno/zeno.h>
#include <zeno/utils/arrayindex.h>
#include <zeno/types/PrimitiveObject.h>
#include <zeno/types/NumericObject.h>
#include <zeno/utils/zeno_p.h>

namespace zeno {

struct CVImageObject : IObjectClone<CVImageObject> {
    cv::Mat image;

    CVImageObject() = default;
    explicit CVImageObject(cv::Mat image) : image(std::move(image)) {}

    CVImageObject(CVImageObject &&) = default;
    CVImageObject &operator=(CVImageObject &&) = default;

    CVImageObject(CVImageObject const &img) : image(img.image.clone()) {
    }

    CVImageObject &operator=(CVImageObject const &img) {
        // notice that cv::Mat is shallow-copy, only .clone() will deep-copy
        image = img.image.clone();
        return *this;
    }
};

namespace {

struct CVINode : INode {
    template <class To = double, class T>
    static auto tocvscalar(T const &val) {
        if constexpr (is_vec_n<T> == 4) {
            return cv::Scalar_<To>(val[3], val[2], val[1], val[0]);
        } else if constexpr (is_vec_n<T> == 3) {
            return cv::Scalar_<To>(val[2], val[1], val[0]);
        } else if constexpr (is_vec_n<T> == 2) {
            return cv::Scalar_<To>(val[1], val[0]);
        } else {
            return To(val);
        }
    }

    //template <class T>
    //static cv::_InputArray tocvinputarr(T const &val) {
        //if constexpr (is_vec_n<T> == 4) {
            //return cv::_InputArray(make_array(val[3], val[2], val[1], val[0]).data(), 4);
        //} else if constexpr (is_vec_n<T> == 3) {
            //return cv::_InputArray(make_array(val[2], val[1], val[0]).data(), 3);
        //} else if constexpr (is_vec_n<T> == 2) {
            //return cv::_InputArray(make_array(val[1], val[0]).data(), 2);
        //} else {
            //return cv::_InputArray((double)val);
        //}
    //}

    cv::Mat get_input_image(std::string const &name, bool inversed = false) {
        //if (has_input<NumericObject>(name)) {
            //auto num = get_input<NumericObject>(name);
            //bool is255 = has_input<NumericObject>("is255") && get_input2<bool>("is255");
            //return 155.f;
            //return std::visit([&] (auto const &val) -> cv::_InputArray {
                //auto tmp = inversed ? 1 - val : val;
                //return tocvinputarr(is255 ? tmp * 255 : tmp);
            //}, num->value);
        //} else {
            if (inversed) {
                cv::Mat newimg;
                auto img = get_input<CVImageObject>(name)->image;
                bool is255 = has_input<NumericObject>("is255") && get_input2<bool>("is255");
                if (is255) {
                    cv::bitwise_not(img, newimg);
                } else {
                    cv::invert(img, newimg);
                }
                return std::move(newimg);
            } else {
                return get_input<CVImageObject>(name)->image;
            }
        //}
    }
};

struct CVImageRead : CVINode {
    void apply() override {
        auto path = get_input2<std::string>("path");
        auto mode = get_input2<std::string>("mode");
        auto is255 = get_input2<bool>("is255");
        cv::ImreadModes flags = array_lookup(
            {cv::IMREAD_COLOR, cv::IMREAD_GRAYSCALE, cv::IMREAD_UNCHANGED, cv::IMREAD_UNCHANGED},
            array_index_safe({"RGB", "GRAY", "RGBA", "UNCHANGED"}, mode, "mode"));
        auto image = std::make_shared<CVImageObject>(cv::imread(path, flags));
        if (image->image.empty()) {
            zeno::log_error(    "opencv failed to read image file: {}", path);
        }
        if (mode == "RGBA") {
            if (is255) {
                image->image.convertTo(image->image, 
                                       image->image.channels() == 1 ? CV_8UC1 :
                                       image->image.channels() == 2 ? CV_8UC2 :
                                       image->image.channels() == 3 ? CV_8UC3 :
                                       CV_8UC4);
            } else {
                image->image.convertTo(image->image, 
                                       image->image.channels() == 1 ? CV_32FC1 :
                                       image->image.channels() == 2 ? CV_32FC2 :
                                       image->image.channels() == 3 ? CV_32FC3 :
                                       CV_32FC4);
            }
            if (image->image.channels() == 1) {
                cv::cvtColor(image->image, image->image, cv::COLOR_GRAY2BGRA);
            } else if (image->image.channels() == 3) {
                cv::cvtColor(image->image, image->image, cv::COLOR_BGR2BGRA);
            }
        } else {
            if (!is255) {
                image->image.convertTo(image->image, 
                                       image->image.channels() == 1 ? CV_32FC1 :
                                       image->image.channels() == 2 ? CV_32FC2 :
                                       image->image.channels() == 3 ? CV_32FC3 :
                                       CV_32FC4);
            }
        }
        set_output("image", std::move(image));
    }
};

ZENDEFNODE(CVImageRead, {
    {
        {"readpath", "path", ""},
        {"enum RGB GRAY RGBA UNCHANGED", "mode", "RGB"},
        {"bool", "is255", "1"},
    },
    {
        {"CVImageObject", "image"},
    },
    {},
    {"opencv"},
});

struct CVSepAlpha : CVINode {
    void apply() override {
        auto image = get_input<CVImageObject>("imageRGBA");
        auto imageRGB = std::make_shared<CVImageObject>();
        cv::cvtColor(image->image, imageRGB->image, cv::COLOR_BGRA2BGR);
        std::vector<cv::Mat> channels;
        cv::split(image->image, channels);
        auto imageAlpha = std::make_shared<CVImageObject>(channels.back());
        if (!get_input2<bool>("alphaAsGray")) {
            cv::cvtColor(imageAlpha->image, imageAlpha->image, cv::COLOR_GRAY2BGR);
        }
        set_output("imageRGB", std::move(imageRGB));
        set_output("imageAlpha", std::move(imageAlpha));
    }
};

ZENDEFNODE(CVSepAlpha, {
    {
        {"CVImageObject", "imageRGBA"},
        {"bool", "alphaAsGray", "0"},
    },
    {
        {"CVImageObject", "imageRGB"},
        {"CVImageObject", "imageAlpha"},
    },
    {},
    {"opencv"},
});

struct CVImageSepRGB : CVINode {
    void apply() override {
        auto image = get_input<CVImageObject>("imageRGB");
        std::vector<cv::Mat> channels;
        cv::split(image->image, channels);
        auto imageB = std::make_shared<CVImageObject>(channels.at(0));
        auto imageG = std::make_shared<CVImageObject>(channels.at(1));
        auto imageR = std::make_shared<CVImageObject>(channels.at(2));
        set_output("imageR", std::move(imageR));
        set_output("imageG", std::move(imageG));
        set_output("imageB", std::move(imageG));
    }
};

ZENDEFNODE(CVImageSepRGB, {
    {
        {"CVImageObject", "imageRGB"},
    },
    {
        {"CVImageObject", "imageR"},
        {"CVImageObject", "imageG"},
        {"CVImageObject", "imageB"},
    },
    {},
    {"opencv"},
});

struct CVImageShow : CVINode {
    void apply() override {
        auto image = get_input_image("image");
        auto title = get_input2<std::string>("title");
        cv::imshow(title, image);
        if (get_input2<bool>("waitKey"))
            cv::waitKey();
    }
};

ZENDEFNODE(CVImageShow, {
    {
        {"CVImageObject", "image"},
        {"string", "title", "imshow"},
        {"bool", "waitKey", "1"},
    },
    {
    },
    {},
    {"opencv"},
});

struct CVWaitKey : CVINode {
    void apply() override {
        auto delay = get_input2<int>("delay");
        int kc = cv::waitKey(delay);
        set_output2("hasPressed", kc != -1);
        set_output2("keyCode", kc);
    }
};

ZENDEFNODE(CVWaitKey, {
    {
        {"int", "delay", "0"},
    },
    {
        {"bool", "hasPressed"},
        {"int", "keyCode"},
    },
    {},
    {"opencv"},
});

struct CVImageAdd : CVINode {
    void apply() override {
        auto image1 = get_input_image("image1");
        auto image2 = get_input_image("image2");
        auto weight1 = get_input2<float>("weight1");
        auto weight2 = get_input2<float>("weight2");
        auto constant = get_input2<float>("constant");
        auto resimage = std::make_shared<CVImageObject>();
        if (weight1 == 1 && weight2 == 1 && constant == 0) {
            cv::add(image1, image2, resimage->image);
        } else {
            cv::addWeighted(image1, weight1, image2, weight2, constant, resimage->image);
        }
        set_output("resimage", std::move(resimage));
    }
};

struct CVImageSubtract : CVINode {
    void apply() override {
        auto image1 = get_input_image("image1");
        auto image2 = get_input_image("image2");
        auto resimage = std::make_shared<CVImageObject>();
        cv::subtract(image1, image2, resimage->image);
        set_output("resimage", std::move(resimage));
    }
};

ZENDEFNODE(CVImageSubtract, {
    {
        {"CVImageObject", "image1"},
        {"CVImageObject", "image2"},
    },
    {
        {"CVImageObject", "resimage"},
    },
    {},
    {"opencv"},
});

struct CVImageMultiply : CVINode {
    void apply() override {
        auto image1 = get_input_image("image");
        auto inverse = get_input2<bool>("inverse");
        auto is255 = get_input2<bool>("is255");
        if (has_input<NumericObject>("factor")) {
            auto factor = get_input2<float>("factor");
            if (inverse) factor = 1 - factor;
            if (is255) factor = 255 * factor;
            auto resimage = std::make_shared<CVImageObject>();
            cv::multiply(image1, factor, resimage->image, is255 ? 1.f / 255.f : 1.f);
            set_output("resimage", std::move(resimage));
        } else {
            auto image2 = get_input_image("factor", inverse);
            auto resimage = std::make_shared<CVImageObject>();
            cv::multiply(image1, image2, resimage->image, is255 ? 1.f / 255.f : 1.f);
            set_output("resimage", std::move(resimage));
        }
    }
};

ZENDEFNODE(CVImageMultiply, {
    {
        {"CVImageObject", "image"},
        {"float"/*or CVImageObject*/, "factor", "1"},
        {"bool", "inverse", "0"},
        {"bool", "is255", "1"},
    },
    {
        {"CVImageObject", "resimage"},
    },
    {},
    {"opencv"},
});

struct CVImageDivide : CVINode {
    void apply() override {
        auto image1 = get_input_image("image");
        auto inverse = get_input2<bool>("inverse");
        auto image2 = get_input_image("factor", inverse);
        auto is255 = get_input2<bool>("is255");
        auto resimage = std::make_shared<CVImageObject>();
        cv::divide(image1, image2, resimage->image, is255 ? 1.f / 255.f : 1.f);
        set_output("resimage", std::move(resimage));
    }
};

ZENDEFNODE(CVImageDivide, {
    {
        {"CVImageObject", "image"},
        {"float"/*or CVImageObject*/, "factor", "1"},
        {"bool", "inverse", "0"},
        {"bool", "is255", "1"},
    },
    {
        {"CVImageObject", "resimage"},
    },
    {},
    {"opencv"},
});

struct CVImageBlend : CVINode {
    void apply() override {
        auto image1 = get_input_image("image1");
        auto image2 = get_input_image("image2");
        auto is255 = get_input2<bool>("is255");
        auto inverse = get_input2<bool>("inverse");
        auto resimage = std::make_shared<CVImageObject>();
        if (inverse) {
            std::swap(image1, image2);
        }
        if (has_input<NumericObject>("factor")) {
            auto factor = get_input2<float>("factor");
            cv::addWeighted(image1, 1 - factor, image2, factor, 0, resimage->image);
        } else {
            auto factor = get_input_image("factor");
            cv::Mat factorinv, tmp1, tmp2;
            if (is255) {
                cv::bitwise_not(factor, factorinv);
            } else {
                cv::invert(factor, factorinv);
            }
            cv::multiply(image1, factorinv, tmp1, is255 ? 1.f / 255.f : 1.f);
            cv::multiply(image2, factor, tmp2, is255 ? 1.f / 255.f : 1.f);
            cv::add(tmp1, tmp2, resimage->image);
        }
        set_output("resimage", std::move(resimage));
    }
};

ZENDEFNODE(CVImageBlend, {
    {
        {"CVImageObject", "image1"},
        {"CVImageObject", "image2"},
        {"float"/*or CVImageObject*/, "factor", "0.5"},
        {"bool", "inverse", "0"},
        {"bool", "is255", "1"},
    },
    {
        {"CVImageObject", "resimage"},
    },
    {},
    {"opencv"},
});

struct CVImageInvert : CVINode {
    void apply() override {
        auto image = get_input_image("image");
        auto is255 = get_input2<bool>("is255");
        auto resimage = std::make_shared<CVImageObject>();
        if (is255) {
            cv::bitwise_not(image, resimage->image);
        } else {
            cv::invert(image, resimage->image);
        }
        set_output("resimage", std::move(resimage));
    }
};

ZENDEFNODE(CVImageInvert, {
    {
        {"CVImageObject", "image"},
        {"bool", "is255", "1"},
    },
    {
        {"CVImageObject", "resimage"},
    },
    {},
    {"opencv"},
});

struct CVConvertColor : CVINode {
    void apply() override {
        auto image = get_input_image("image");
        auto mode = get_input2<std::string>("mode");
        cv::ColorConversionCodes code = array_lookup({
            cv::COLOR_BGR2GRAY,
            cv::COLOR_GRAY2BGR,
            cv::COLOR_BGR2RGB,
            cv::COLOR_BGR2BGRA,
            cv::COLOR_BGRA2BGR,
            cv::COLOR_BGR2HSV,
            cv::COLOR_HSV2BGR,
        }, array_index_safe({
            "BGR2GRAY",
            "GRAY2BGR",
            "BGR2RGB",
            "BGR2BGRA",
            "BGRA2BGR",
            "BGR2HSV",
            "HSV2BGR",
        }, mode, "mode"));
        auto resimage = std::make_shared<CVImageObject>();
        cv::cvtColor(image, resimage->image, code);
        set_output("resimage", std::move(resimage));
    }
};

ZENDEFNODE(CVConvertColor, {
    {
        {"CVImageObject", "image"},
        {
            "enum "
            "BGR2GRAY "
            "GRAY2BGR "
            "BGR2RGB "
            "BGR2BGRA "
            "BGRA2BGR "
            "BGR2HSV "
            "HSV2BGR "
            , "mode", "GRAY2BGR"},
    },
    {
        {"CVImageObject", "resimage"},
    },
    {},
    {"opencv"},
});

struct CVImageGrayscale : CVINode {
    void apply() override {
        auto image = get_input_image("image");
        auto resimage = std::make_shared<CVImageObject>();
        cv::Mat tmp;
        cv::cvtColor(image, tmp, cv::COLOR_BGR2GRAY);
        cv::cvtColor(tmp, resimage->image, cv::COLOR_GRAY2BGR);
        set_output("resimage", std::move(resimage));
    }
};

ZENDEFNODE(CVImageGrayscale, {
    {
        {"CVImageObject", "image"},
    },
    {
        {"CVImageObject", "resimage"},
    },
    {},
    {"opencv"},
});

struct CVImageFillColor : CVINode {
    void apply() override {
        auto likeimage = get_input<CVImageObject>("image");
        auto is255 = get_input2<bool>("is255");
        auto color = tocvscalar<float>(get_input2<vec3f>("color"));
        auto image = get_input2<bool>("inplace") ? likeimage
            : std::make_shared<CVImageObject>(likeimage->image.clone());
        if (has_input("mask")) {
            auto mask = get_input<CVImageObject>("mask");
            if (is255) {
                cv::Point3_<unsigned char> cval;
                cval.x = (unsigned char)std::clamp(color[0] * 255.f, 0.f, 255.f);
                cval.y = (unsigned char)std::clamp(color[1] * 255.f, 0.f, 255.f);
                cval.z = (unsigned char)std::clamp(color[2] * 255.f, 0.f, 255.f);
                image->image.setTo(cv::Scalar(cval.x, cval.y, cval.z), mask->image);
            } else {
                image->image.setTo(cv::Scalar(color[0], color[1], color[2]), mask->image);
            }
        } else {
            if (is255) {
                cv::Point3_<unsigned char> cval;
                cval.x = (unsigned char)std::clamp(color[0] * 255.f, 0.f, 255.f);
                cval.y = (unsigned char)std::clamp(color[1] * 255.f, 0.f, 255.f);
                cval.z = (unsigned char)std::clamp(color[2] * 255.f, 0.f, 255.f);
                image->image.setTo(cv::Scalar(cval.x, cval.y, cval.z));
            } else {
                image->image.setTo(cv::Scalar(color[0], color[1], color[2]));
            }
        }
        set_output("image", std::move(image));
    }
};

ZENDEFNODE(CVImageFillColor, {
    {
        {"CVImageObject", "image"},
        {"optional CVImageObject", "mask"},
        {"bool", "is255", "1"},
        {"vec3f", "color", "1,1,1"},
        {"bool", "inplace", "0"},
    },
    {
        {"CVImageObject", "image"},
    },
    {},
    {"opencv"},
});

struct CVImageMaskedAssign : CVINode {
    void apply() override {
        auto likeimage = get_input<CVImageObject>("image");
        auto srcimage = get_input<CVImageObject>("srcImage");
        auto is255 = get_input2<bool>("is255");
        auto image = get_input2<bool>("inplace") ? likeimage
            : std::make_shared<CVImageObject>(likeimage->image.clone());
        if (has_input("mask")) {
            auto mask = get_input<CVImageObject>("mask");
            image->image.setTo(srcimage->image, mask->image);
        } else {
            image->image.setTo(srcimage->image);
        }
        set_output("image", std::move(image));
    }
};

ZENDEFNODE(CVImageMaskedAssign, {
    {
        {"CVImageObject", "image"},
        {"CVImageObject", "srcImage"},
        {"optional CVImageObject", "mask"},
        {"bool", "is255", "1"},
        {"bool", "inplace", "0"},
    },
    {
        {"CVImageObject", "image"},
    },
    {},
    {"opencv"},
});

struct CVImageBlit : CVINode {
    void apply() override {
        auto likeimage = get_input<CVImageObject>("image");
        auto srcimage = get_input<CVImageObject>("srcImage");
        auto is255 = get_input2<bool>("is255");
        auto centered = get_input2<bool>("centered");
        auto image = get_input2<bool>("inplace") ? likeimage
            : std::make_shared<CVImageObject>(likeimage->image.clone());
        auto x0 = get_input2<int>("X0");
        auto y0 = get_input2<int>("Y0");
        auto dx = srcimage->image.cols;
        auto dy = srcimage->image.rows;
        auto maxx = image->image.cols;
        auto maxy = image->image.rows;
        if (centered) {
            x0 += dx / 2;
            y0 += dy / 2;
        }
        //zeno::log_warn("dx {} dy {}", dx, dy);
        int sx0 = 0, sy0 = 0;
        bool hasmodroi = false;
        if (x0 < 0) {
            dx -= -x0;
            sx0 = -x0;
            x0 = 0;
            hasmodroi = true;
        }
        if (y0 < 0) {
            dy -= -y0;
            sy0 = -y0;
            y0 = 0;
            hasmodroi = true;
        }
        if (x0 + dx > maxx) {
            dx = maxx - x0;
            hasmodroi = true;
        }
        if (y0 + dy > maxy) {
            dy = maxy - y0;
            hasmodroi = true;
        }
        //zeno::log_warn("x0 {} y0 {} dx {} dy {} sx0 {} sy0 {}", x0, y0, dx, dy, sx0, sy0);
        cv::Rect roirect(x0, y0, dx, dy);
        auto roi = image->image(roirect);
        auto srcroi = srcimage->image;
        if (hasmodroi) {
            srcroi = srcroi(cv::Rect(sx0, sy0, dx, dy));
        }
        if (has_input("mask")) {
            auto mask = get_input<CVImageObject>("mask");
            auto factor = mask->image;
            if (hasmodroi) {
                factor = factor(cv::Rect(sx0, sy0, dx, dy));
            }
            if (get_input2<bool>("isAlphaMask")) {
                auto image1 = roi, image2 = srcroi;
                cv::Mat factorinv, tmp1, tmp2;
                if (is255) {
                    cv::bitwise_not(factor, factorinv);
                } else {
                    cv::invert(factor, factorinv);
                }
                cv::multiply(image1, factorinv, tmp1, is255 ? 1.f / 255.f : 1.f);
                cv::multiply(image2, factor, tmp2, is255 ? 1.f / 255.f : 1.f);
                cv::add(tmp1, tmp2, tmp2);
                tmp2.copyTo(roi);
            } else {
                srcroi.copyTo(roi, factor);
            }
        } else {
            srcroi.copyTo(roi);
        }
        set_output("image", std::move(image));
    }
};

ZENDEFNODE(CVImageBlit, {
    {
        {"CVImageObject", "image"},
        {"CVImageObject", "srcImage"},
        {"int", "X0", "0"},
        {"int", "Y0", "0"},
        {"bool", "centered", "0"},
        {"optional CVImageObject", "mask"},
        {"bool", "isAlphaMask", "1"},
        {"bool", "is255", "1"},
        {"bool", "inplace", "0"},
    },
    {
        {"CVImageObject", "image"},
    },
    {},
    {"opencv"},
});

struct CVImageCrop : CVINode {
    void apply() override {
        auto srcimage = get_input<CVImageObject>("srcimage");
        auto is255 = get_input2<bool>("is255");
        auto isDeep = get_input2<bool>("deepCopy");
        auto x0 = get_input2<int>("X0");
        auto y0 = get_input2<int>("Y0");
        auto dx = get_input2<int>("DX");
        auto dy = get_input2<int>("DY");
        cv::Rect roirect(x0, y0, dx, dy);
        auto roi = srcimage->image(roirect);
        if (isDeep) roi = roi.clone();
        auto image = std::make_shared<CVImageObject>(std::move(roi));
        set_output("image", std::move(image));
    }
};

ZENDEFNODE(CVImageCrop, {
    {
        {"CVImageObject", "srcimage"},
        {"int", "X0", "0"},
        {"int", "Y0", "0"},
        {"int", "DX", "32"},
        {"int", "DY", "32"},
        {"bool", "is255", "1"},
        {"bool", "deepCopy", "1"},
    },
    {
        {"CVImageObject", "image"},
    },
    {},
    {"opencv"},
});

struct CVMakeImage : CVINode {
    void apply() override {
        auto likeimage = get_input<CVImageObject>("image");
        auto srcimage = get_input<CVImageObject>("srcImage");
        auto mode = get_input2<std::string>("mode");
        auto isWhite = get_input2<bool>("whiteBg");
        auto is255 = get_input2<bool>("is255");
        auto w = get_input2<int>("width");
        auto h = get_input2<int>("height");
        int ty = array_lookup(is255 ?
                              make_array(CV_8UC3, CV_8UC1, CV_8UC4) :
                              make_array(CV_32FC3, CV_32FC1, CV_32FC4),
            array_index_safe({"RGB", "GRAY", "RGBA"}, mode, "mode"));
        auto image = std::make_shared<CVImageObject>(cv::Mat(h, w, ty, cv::Scalar::all(
                    isWhite ? (is255 ? 1 : 255) : 0)));
        set_output("image", std::move(image));
    }
};

ZENDEFNODE(CVMakeImage, {
    {
        {"int", "width", "512"},
        {"int", "height", "512"},
        {"enum RGB GRAY RGBA", "mode", "RGB"},
        {"bool", "whiteBg", "0"},
        {"bool", "is255", "1"},
    },
    {
        {"CVImageObject", "image"},
    },
    {},
    {"opencv"},
});

struct CVGetImageSize : CVINode {
    void apply() override {
        auto image = get_input<CVImageObject>("image");
        set_output2("width", image->image.cols);
        set_output2("height", image->image.rows);
        set_output2("channels", image->image.channels());
    }
};

ZENDEFNODE(CVGetImageSize, {
    {
        {"CVImageObject", "image"},
    },
    {
        {"int", "width"},
        {"int", "height"},
        {"int", "channels"},
    },
    {},
    {"opencv"},
});

struct CVImageFillGrad : CVINode {
    void apply() override {
        auto likeimage = get_input<CVImageObject>("image");
        auto angle = get_input2<float>("angle");
        auto scale = get_input2<float>("scale");
        auto offset = get_input2<float>("offset");
        auto is255 = get_input2<bool>("is255");
        auto color1 = tocvscalar<float>(get_input2<vec3f>("color1"));
        auto color2 = tocvscalar<float>(get_input2<vec3f>("color2"));
        auto image = get_input2<bool>("inplace") ? likeimage
            : std::make_shared<CVImageObject>(likeimage->image.clone());
        vec2i shape(image->image.size[1], image->image.size[0]);
        vec2f invshape = 1.f / shape;
        angle *= (std::atan(1.f) * 4) / 180;
        vec2f dir(std::cos(angle), std::sin(angle));
        auto invscale = 0.5f / scale;
        auto neoffset = 0.5f - (offset * 2 - 1) * invscale;
        if (is255) {
            image->image.forEach<cv::Point3_<unsigned char>>([&] (cv::Point3_<unsigned char> &val, const int *pos) {
                vec2i posv(pos[1], pos[0]);
                float f = dot(posv * invshape * 2 - 1, dir) * invscale + neoffset, omf = 1 - f;
                val.x = (unsigned char)std::clamp((omf * color1[0] + f * color2[0]) * 255.f, 0.f, 255.f);
                val.y = (unsigned char)std::clamp((omf * color1[1] + f * color2[1]) * 255.f, 0.f, 255.f);
                val.z = (unsigned char)std::clamp((omf * color1[2] + f * color2[2]) * 255.f, 0.f, 255.f);
            });
        } else {
            image->image.forEach<cv::Point3_<float>>([&] (cv::Point3_<float> &val, const int *pos) {
                vec2i posv(pos[1], pos[0]);
                float f = dot(posv * invshape * 2 - 1, dir) * invscale + neoffset, omf = 1 - f;
                val.x = omf * color1[0] + f * color2[0];
                val.y = omf * color1[1] + f * color2[1];
                val.z = omf * color1[2] + f * color2[2];
            });
        }
        set_output("image", std::move(image));
    }
};

ZENDEFNODE(CVImageFillGrad, {
    {
        {"CVImageObject", "image"},
        {"float", "angle", "0"},     // rotation clock-wise
        {"float", "scale", "1"},     // thickness of gradient
        {"float", "offset", "0.5"},  // 0 to 1
        {"bool", "is255", "1"},
        {"vec3f", "color1", "0,0,0"},
        {"vec3f", "color2", "1,1,1"},
        {"bool", "inplace", "0"},
    },
    {
        {"CVImageObject", "image"},
    },
    {},
    {"opencv"},
});

struct CVImageDrawPoly : CVINode {
    void apply() override {
        auto image = get_input<CVImageObject>("image");
        auto color = tocvscalar<float>(get_input2<vec3f>("color"));
        if (!get_input2<bool>("inplace"))
            image = std::make_shared<CVImageObject>(*image);
        auto prim = get_input<PrimitiveObject>("prim");
        auto linewidth = get_input2<int>("linewidth");
        auto batched = get_input2<bool>("batched");
        auto antialias = get_input2<bool>("antialias");
        auto is255 = get_input2<bool>("is255");
        if (is255) color *= 255.f;
            //image->image.setTo(cv::Scalar::all(0));
        vec2i shape(image->image.size[1], image->image.size[0]);

        std::vector<std::vector<cv::Point>> vpts(prim->polys.size());
        for (int i = 0; i < prim->polys.size(); i++) {
            auto [base, len] = prim->polys[i];
            auto &pt = vpts[i];
            pt.resize(len);
            for (int k = 0; k < len; k++) {
                auto v = prim->verts[prim->loops[base + k]];
                pt[k].x = int((v[0] * 0.5f + 0.5f) * shape[0]);
                pt[k].y = int((v[1] * -0.5f + 0.5f) * shape[1]);
            }
        }
        std::vector<const cv::Point *> pts(vpts.size());
        std::vector<int> npts(vpts.size());
        for (int i = 0; i < vpts.size(); i++) {
            pts[i] = vpts[i].data();
            npts[i] = vpts[i].size();
        }

        cv::LineTypes linemode = antialias ? cv::LINE_AA : cv::LINE_4;
        if (linewidth > 0) {
            if (batched) {
                cv::polylines(image->image, pts.data(), npts.data(), npts.size(), 0, color, linewidth, linemode);
            } else {
                for (int i = 0; i < npts.size(); i++) {
                    cv::polylines(image->image, pts.data() + i, npts.data() + i, 1, 0, color, linewidth, linemode);
                }
            }
        } else {
            if (batched) {
                cv::fillPoly(image->image, pts.data(), npts.data(), npts.size(), color, linemode);
            } else {
                for (int i = 0; i < npts.size(); i++) {
                    cv::fillPoly(image->image, pts.data() + i, npts.data() + i, 1, color, linemode);
                }
            }
        }
        set_output("image", std::move(image));
    }
};

ZENDEFNODE(CVImageDrawPoly, {
    {
        {"CVImageObject", "image"},
        {"PrimitiveObject", "prim"},
        {"vec3f", "color", "1,1,1"},
        {"PrimitiveObject", "points"},
        {"int", "linewidth", "0"},
        {"bool", "inplace", "0"},
        {"bool", "batched", "0"},
        {"bool", "antialias", "0"},
        {"bool", "is255", "1"},
    },
    {
        {"CVImageObject", "image"},
    },
    {},
    {"opencv"},
});

struct CVImagePutText : CVINode {
    void apply() override {
        auto likeimage = get_input<CVImageObject>("image");
        auto image = get_input2<bool>("inplace") ? likeimage
            : std::make_shared<CVImageObject>(likeimage->image.clone());
        auto text = get_input2<std::string>("text");
        auto fontFace = get_input2<int>("fontFace");
        auto thickness = get_input2<int>("thickness");
        auto antialias = get_input2<bool>("antialias");
        auto scale = get_input2<float>("scale");
        auto is255 = get_input2<bool>("is255");
        auto color = tocvscalar<double>(get_input2<vec3f>("color") * (is255 ? 255 : 1));
        cv::Point org(get_input2<int>("X0"), get_input2<int>("Y0"));
        cv::putText(image->image, text, org, fontFace, scale, color,
                    thickness, antialias ? cv::LINE_AA : cv::LINE_8);
        set_output("resimage", std::move(image));
    }
};

ZENDEFNODE(CVImagePutText, {
    {
        {"CVImageObject", "image"},
        {"string", "text", "Hello, World"},
        {"int", "X0", "0"},
        {"int", "Y0", "0"},
        {"bool", "is255", "1"},
        {"vec3f", "color", "1,1,1"},
        {"float", "scale", "1"},
        {"int", "thickness", "1"},
        {"int", "fontFace", "0"},
        {"bool", "antialias", "0"},
        {"bool", "inplace", "0"},
    },
    {
        {"CVImageObject", "resimage"},
    },
    {},
    {"opencv"},
});

}

}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值