Image.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/UserData.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);
}
}
cv::Mat get_input_image(std::string const &name, bool inversed = false) {
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 ImageRead : CVINode {
void apply() override {
auto path = get_input2<std::string>("path");
auto mode = get_input2<std::string>("mode");
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);
}
set_output("image", std::move(image));
}
};
ZENDEFNODE(ImageRead, {
{
{"readpath", "path", ""},
{"enum RGB GRAY RGBA UNCHANGED", "mode", "RGB"},
},
{
{"CVImageObject", "image"},
},
{},
{""},
});
struct ImageShow : 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(ImageShow, {
{
{"CVImageObject", "image"},
{"string", "title", "imshow"},
{"bool", "waitKey", "1"},
},
{
},
{},
{""},
});
/* 导入地形网格的属性,可能会有多个属性。它将地形的属性转换为图
像,每个属性对应一个图层。
可能需要的参数:outRemapRange,分辨率,属性名称,属性数据
类型为float32 */
struct CompImport : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(CompImport, {
{
},
{},
{},
{ "" },
});
/* 删除指定的图层(属性)。需要指定图层的名称(可能会有多个),选
项:删除选择/未选择图层 */
struct CompDelete : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(CompDelete, {
{
},
{
{"image"}
},
{},
{ "" },
});
/* 重命名图层,可能需要的参数:源名称,目标名称 */
struct CompRename : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(CompRename, {
{
},
{
{"image"}
},
{},
{ "" },
});
/* 创建颜色图层,可能需要的参数:颜色,分辨率,图层名称 */
struct CompColor : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(CompColor, {
{
{"image"}
},
{
{"image"}
},
{},
{ "" },
});
struct comp_color_ramp : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(comp_color_ramp, {
{
{"image"}
},
{
{"image"}
},
{},
{ "" },
});
/* 合成图层。可能需要的参数:前景图层,前景权重,背景图层,背景
权重,Mask图层,Mask权重
用 mask 将前景和背景叠加的操作算法(操作分别作用在前背景和alpha通道上):
mask可以被忽略,它只是将操作限制在图像的一个区域。mask可
以是反转的、变亮的或变暗的。
mask可以单独指定,或者来自前景的alpha通道。
算法参考:https://www.deanhan.cn/canvas-blende-mode.html
over:将前景放置于背景之上
under:将前景放置在背景的alpha之下
atop:当背景的alpha存在的时候,才将前景放置于背景之上
inside:将前景放置在背景的alpha中。
outside:将前景放置在背景的alpha之外
screen:作用与饱和度add相同
add:将前景添加到背景上
subtract:从背景中减去前景
diff:获取前景和背景之间的差的绝对值
myltiply:将背景与前景相乘
minimum:取前景和背景的最小值
maximum:取前景和背景的最大值
average:取前景和背景的平均值
xor:异或运算,对两个Alpha平面进行异或运算,以便删除重叠的
Alpha区域
图像过滤器(可能在分辨率不统一的时候使用) */
struct CompComposite : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(CompComposite, {
{
{"image"}
},
{
{"image"}
},
{},
{ "" },
});
/* 图像对比度调节
此操作可增加或降低图像的对比度。这可以通过两种方式实现:
范围-通过设置原始黑白的新值。该范围将被重新映射以适应新值。
缩放-通过拾取中心轴(通常为0.5)并围绕该值进行缩放。 */
struct CompContrast : INode {
virtual void apply() override {
auto image = get_input<PrimitiveObject>("image");
float ContrastRatio = get_input2<float>("ContrastRatio");
auto &ud = image->userData();
int w = ud.get2<int>("w");
int h = ud.get2<int>("h");
for (auto i = 0; i < image->verts.size(); i++) {
image->verts[i] = image->verts[i]+(image->verts[i]-0.5) * ContrastRatio;
}
set_output("image", image);
}
};
ZENDEFNODE(CompContrast, {
{
{"image"},
{"float", "ContrastRatio", "1"},
},
{"image"},
{},
{ "" },
});
/* 图像饱和度调节() */
struct CompSaturation : INode {
virtual void apply() override {
auto image = get_input<PrimitiveObject>("image");
float Si = get_input2<float>("Saturation");
auto &ud = image->userData();
int w = ud.get2<int>("w");
int h = ud.get2<int>("h");
for (auto i = 0; i < image->verts.size(); i++) {
float R = image->verts[i][0];
float G = image->verts[i][1];
float B = image->verts[i][2];
// float S = Si * (1-(3 * zeno::min(zeno::min(R,G),B))/(R+G+B));
// float V = (R+G+B)/3;
// float m = V -
image->verts[i][0] = R * Si;
image->verts[i][1] = G * Si;
image->verts[i][2] = B * Si;
}
set_output("image", image);
}
};
ZENDEFNODE(CompSaturation, {
{
{"image"},
{"float", "ContrastRatio", "1"},
},
{"image"},
{},
{ "" },
});
/*边缘查找*/
struct comp_edge_detect : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(comp_edge_detect, {
{
{"image"}
},
{},
{},
{ "" },
});
struct CompEdgeDetect : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(CompEdgeDetect, {
{
{"image"}
},
{},
{},
{ "" },
});
/* 图像模糊,可以使用不同的卷积核(Gaussian) */
struct CompBlur : CVINode {
virtual void apply() override {
auto image = get_input<PrimitiveObject>("image");
float ContrastRatio = get_input2<float>("ContrastRatio");
auto &ud = image->userData();
int w = ud.get2<int>("w");
int h = ud.get2<int>("h");
for (auto i = 0; i < image->verts.size(); i++) {
image->verts[i] = image->verts[i]+(image->verts[i]-0.5) * ContrastRatio;
}
set_output("image", image);
}
};
ZENDEFNODE(CompBlur, {
{
{"image"}
},
{
{"image"}
},
{},
{ "" },
});
/* 对图像应用像素反转,本质上是 Clr_out = 1 - Clr_in */
struct CompInvert : CVINode{
virtual void apply() override {
auto image = get_input<PrimitiveObject>("image");
auto &ud = image->userData();
int w = ud.get2<int>("w");
int h = ud.get2<int>("h");
for (auto i = 0; i < image->verts.size(); i++) {
image->verts[i] = 1 - image->verts[i];
}
set_output("image", image);
}
};
ZENDEFNODE(CompInvert, {
{
{"image"},
},
{
"image",
},
{},
{""},
});
/* 将灰度图像转换为法线贴图 */
struct CompNormalMap : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(CompNormalMap, {
{
{"image"}
},
{
{"image"}
},
{},
{ "" },
});
/* 此操作将颜色或向量转换为标量,如亮度或长度。或者,可以将向量
平面转换为标量平面。 */
struct CompAverage : INode {
virtual void apply() override {
auto image = get_input<PrimitiveObject>("image");
auto &ud = image->userData();
int w = ud.get2<int>("w");
int h = ud.get2<int>("h");
for (auto i = 0; i < image->verts.size(); i++) {
vec3f rgb = image->verts[i];
float avg = (rgb[0] + rgb[1] + rgb[2]) / 3;
image->verts[i] = {avg, avg, avg};
}
set_output("image", image);
}
};
ZENDEFNODE(CompAverage, {
{
{"image"}
},
{
{"image"}
},
{},
{ "" },
});
/* 调整黑点、白点和中值以增加、平衡或降低对比度。
您可以使用Value选项卡全局调整级别(影响所有通道),或使用R、
G、B或Comp 4选项卡逐个通道调整。输入级别用于压缩黑点和白点
范围,从而增加对比度。
Gamma将作为使用输入级别指定的范围的中值调整进行应用。输出级
别扩展了黑白点范围,降低了对比度。 */
struct CompLevels : CVINode {
virtual void apply() override {
auto image = get_input<PrimitiveObject>("image");
auto &ud = image->userData();
int w = ud.get2<int>("w");
int h = ud.get2<int>("h");
for (auto i = 0; i < image->verts.size(); i++) {
}
set_output("image", image);
}
};
ZENDEFNODE(CompLevels, {
{
{"image"}
},
{
{"image"}
},
{},
{ "" },
});
//RGB2YUV BT.709标准
struct ImageEdit_YUV : CVINode {
virtual void apply() override {
auto image = get_input<PrimitiveObject>("image");
auto RGB = get_input2<std::string>("RGB");
auto Gray = get_input2<bool>("Gray_BT.709");
auto Average = get_input2<bool>("Average");
auto Invert = get_input2<bool>("Invert");
float R = get_input2<float>("R");
float G = get_input2<float>("G");
float B = get_input2<float>("B");
float L = get_input2<float>("Luminace_BT.709");
float Si = get_input2<float>("Luminace_HSV");
float ContrastRatio = get_input2<float>("ContrastRatio");
auto &ud = image->userData();
int w = ud.get2<int>("w");
int h = ud.get2<int>("h");
if(RGB == "RGB") {
for (auto i = 0; i < image->verts.size(); i++) {
float R1 = R * image->verts[i][0];
float G1 = G * image->verts[i][1];
float B1 = B * image->verts[i][2];
float Y = L * (0.2126 * R1 + 0.7152 * G1 + 0.0722 * B1);
float U = -0.1145 * R1 - 0.3855 * G1 + 0.500 * B1;
float V = 0.500 * R1 - 0.4543 * G1 - 0.0457 * B1;
image->verts[i][0] = Y + 1.5748 * V;
image->verts[i][1] = Y - 0.1868 * U - 0.4680 * V;
image->verts[i][2] = Y + 1.856 * U;
}
}
if(RGB == "R") {
for (auto i = 0; i < image->verts.size(); i++) {
float R1 = R * image->verts[i][0];
float G1 = 0;
float B1 = 0;
float Y = L * (0.2126 * R1 + 0.7152 * G1 + 0.0722 * B1);
float U = -0.1145 * R1 - 0.3855 * G1 + 0.500 * B1;
float V = 0.500 * R1 - 0.4543 * G1 - 0.0457 * B1;
image->verts[i][0] = Y + 1.5748 * V;
image->verts[i][1] = Y - 0.1868 * U - 0.4680 * V;
image->verts[i][2] = Y + 1.856 * U;
}
}
if(RGB == "G") {
for (auto i = 0; i < image->verts.size(); i++) {
float R1 = 0;
float G1 = G * image->verts[i][1];
float B1 = 0;
float Y = L * (0.2126 * R1 + 0.7152 * G1 + 0.0722 * B1);
float U = -0.1145 * R1 - 0.3855 * G1 + 0.500 * B1;
float V = 0.500 * R1 - 0.4543 * G1 - 0.0457 * B1;
image->verts[i][0] = Y + 1.5748 * V;
image->verts[i][1] = Y - 0.1868 * U - 0.4680 * V;
image->verts[i][2] = Y + 1.856 * U;
}
}
if(RGB == "B") {
for (auto i = 0; i < image->verts.size(); i++) {
float R1 = 0;
float G1 = 0;
float B1 = B * image->verts[i][2];
float Y = L * (0.2126 * R1 + 0.7152 * G1 + 0.0722 * B1);
float U = -0.1145 * R1 - 0.3855 * G1 + 0.500 * B1;
float V = 0.500 * R1 - 0.4543 * G1 - 0.0457 * B1;
image->verts[i][0] = Y + 1.5748 * V;
image->verts[i][1] = Y - 0.1868 * U - 0.4680 * V;
image->verts[i][2] = Y + 1.856 * U;
}
}
for (auto i = 0; i < image->verts.size(); i++) {
float R3 = image->verts[i][0];
float G3 = image->verts[i][1];
float B3 = image->verts[i][2];
image->verts[i] = image->verts[i]+(image->verts[i]-0.5) * ContrastRatio;
image->verts[i][0] = R3 * Si;
image->verts[i][1] = G3 * Si;
image->verts[i][2] = B3 * Si;
}
if(Gray){
for (auto i = 0; i < image->verts.size(); i++) {
float R = image->verts[i][0];
float G = image->verts[i][1];
float B = image->verts[i][2];
image->verts[i][0] = 0.2126 * R + 0.7152 * G + 0.0722 * B ;
image->verts[i][1] = 0.2126 * R + 0.7152 * G + 0.0722 * B ;
image->verts[i][2] = 0.2126 * R + 0.7152 * G + 0.0722 * B ;
}
}
if(Average){
for (auto i = 0; i < image->verts.size(); i++) {
float avr = (R + G + B)/3;
}
}
if(Invert){
for (auto i = 0; i < image->verts.size(); i++) {
image->verts[i] = 1 - image->verts[i];
}
}
set_output("image", image);
}
};
ZENDEFNODE(ImageEdit_YUV, {
{
{"image"},
{"enum RGB R G B", "RGB", "RGB"},
{"float", "R", "1"},
{"float", "G", "1"},
{"float", "B", "1"},
{"float", "Luminace_BT.709", "1"},
{"float", "ContrastRatio", "1"},
{"bool", "Gray_BT.709", "0"},
{"bool", "Average", "0"},
{"bool", "Invert", "0"},
},
{
{"image"}
},
{},
{ "comp" },
});
struct ImageEdit_RGB : CVINode {
virtual void apply() override {
auto image = get_input<PrimitiveObject>("image");
auto RGB = get_input2<std::string>("RGB");
auto Average = get_input2<bool>("Average");
auto Invert = get_input2<bool>("Invert");
float R = get_input2<float>("R");
float G = get_input2<float>("G");
float B = get_input2<float>("B");
float L = get_input2<float>("Luminace_RGB");
float ContrastRatio = get_input2<float>("ContrastRatio");
auto &ud = image->userData();
int w = ud.get2<int>("w");
int h = ud.get2<int>("h");
if(RGB == "RGB") {
for (auto i = 0; i < image->verts.size(); i++) {
float R1 = R * image->verts[i][0];
float G1 = G * image->verts[i][1];
float B1 = B * image->verts[i][2];
image->verts[i][0] = R1 * L;
image->verts[i][1] = G1 * L;
image->verts[i][2] = B1 * L;
}
}
if(RGB == "R") {
for (auto i = 0; i < image->verts.size(); i++) {
float R1 = R * image->verts[i][0];
float G1 = 0;
float B1 = 0;
image->verts[i][0] = R1 * L;
image->verts[i][1] = G1 * L;
image->verts[i][2] = B1 * L;
}
}
if(RGB == "G") {
for (auto i = 0; i < image->verts.size(); i++) {
float R1 = 0;
float G1 = G * image->verts[i][1];
float B1 = 0;
image->verts[i][0] = R1 * L;
image->verts[i][1] = G1 * L;
image->verts[i][2] = B1 * L;
}
}
if(RGB == "B") {
for (auto i = 0; i < image->verts.size(); i++) {
float R1 = 0;
float G1 = 0;
float B1 = B * image->verts[i][2];
image->verts[i][0] = R1 * L;
image->verts[i][1] = G1 * L;
image->verts[i][2] = B1 * L;
}
}
for (auto i = 0; i < image->verts.size(); i++) {
image->verts[i] = image->verts[i]+(image->verts[i]-0.5) * ContrastRatio;
}
if(Average){
for (auto i = 0; i < image->verts.size(); i++) {
float R = image->verts[i][0];
float G = image->verts[i][1];
float B = image->verts[i][2];
float avr = (R + G + B)/3;
image->verts[i][0] = avr ;
image->verts[i][1] = avr ;
image->verts[i][2] = avr ;
}
}
if(Invert){
for (auto i = 0; i < image->verts.size(); i++) {
image->verts[i] = 1 - image->verts[i];
}
}
set_output("image", image);
}
};
ZENDEFNODE(ImageEdit_RGB, {
{
{"image"},
{"enum RGB R G B", "RGB", "RGB"},
{"float", "R", "1"},
{"float", "G", "1"},
{"float", "B", "1"},
{"float", "Luminace_RGB", "1"},
{"float", "ContrastRatio", "1"},
{"bool", "Average", "0"},
{"bool", "Invert", "0"},
},
{
{"image"}
},
{},
{ "comp" },
});
/* 此操作将输入数据量化为离散的步骤,从而降低图像中的颜色级别。 */
struct comp_quantize : CVINode {
virtual void apply() override {
}
};
ZENDEFNODE(comp_quantize, {
{
},
{},
{},
{ "image" },
});
}
}
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"},
});
}
}