(6条消息) 【开发日志】2023.04 ZENO----Composite----Combine composite123_EndlessDaydream的博客-CSDN博客https://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"},
});
}
}