1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
#ifndef ROTOR_TRANSFORMS
#define ROTOR_TRANSFORMS
#include "rotor.h"
namespace Rotor {
#define TRANSFORM_nearest 1
#define TRANSFORM_linear 2
#define TRANSFORM_area 3
#define TRANSFORM_cubic 4
#define TRANSFORM_lanczos 5
class Transformer: public Image_node {
//base class for nodes that transform
//what is the best coordinate system to use?
//origin: corner or centre
//units: pixel or fractional
//aspect: scaled or homogenous
public:
Transformer(){
create_parameter("transformX","number","X transformation","Transform X",0.0f);
create_parameter("transformY","number","Y transformation","Transform X",0.0f);
create_parameter("originX","number","X transformation origin","Origin X",0.5f);
create_parameter("originY","number","Y transformation origin","Origin Y",0.5f);
create_parameter("rotation","number","Rotation about origin","Rotation",0.0f);
create_parameter("scale","number","Scale about origin","Scale",1.0f);
create_attribute("filter","Filtering mode","Filter mode","linear",{"nearest","linear","area","cubic","lanczos"});
};
Transformer(map<string,string> &settings):Transformer() {
base_settings(settings);
};
~Transformer(){
};
Image *transform(Image *in){
if (in){
//INTER_NEAREST - a nearest-neighbor interpolation
//INTER_LINEAR - a bilinear interpolation (used by default)
//INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
//INTER_CUBIC - a bicubic interpolation over 4x4 pixel neighborhood
//INTER_LANCZOS4 - a Lanczos interpolation over 8x8 pixel neighborhood
int filtmode;
switch(attributes["filter"]->intVal){
case TRANSFORM_nearest:
filtmode=cv::INTER_NEAREST;
break;
case TRANSFORM_linear:
filtmode=cv::INTER_LINEAR;
break;
case TRANSFORM_area:
filtmode=cv::INTER_AREA;
break;
case TRANSFORM_cubic:
filtmode=cv::INTER_CUBIC;
break;
case TRANSFORM_lanczos:
filtmode=cv::INTER_LANCZOS4;
break;
}
float tX=parameters["transformX"]->value;
float tY=parameters["transformY"]->value;
float oX=parameters["originX"]->value;
float oY=parameters["originY"]->value;
float r=parameters["rotation"]->value;
float s=parameters["scale"]->value;
//do opencv transform
cv::Point2f srcTri[3], dstTri[3];
cv::Mat rot_mat(2,3,CV_32FC1);
cv::Mat trans_mat(2,3,CV_32FC1);
Image inter;
inter.setup(in->w,in->h);
// Compute matrix by creating triangle and transforming
//is there a better way - combine the 2? Just a bit of geometry
srcTri[0].x=0;
srcTri[0].y=0;
srcTri[1].x=in->w-1;
srcTri[1].y=0;
srcTri[2].x=0;
srcTri[2].y=in->h-1;
for (int i=0;i<3;i++){
dstTri[i].x=srcTri[i].x+(tX*in->w);
dstTri[i].y=srcTri[i].y+(tY*in->h);
}
trans_mat=getAffineTransform( srcTri, dstTri );
warpAffine( in->rgb, inter.rgb, trans_mat, inter.rgb.size(), filtmode, cv::BORDER_WRAP);
// Compute rotation matrix
//
cv::Point centre = cv::Point( oX*image.w, oY*image.h );
rot_mat = getRotationMatrix2D( centre, r, s );
// Do the transformation
//
warpAffine( inter.rgb, image.rgb, rot_mat, image.rgb.size(), filtmode, cv::BORDER_WRAP);
//BORDER_WRAP
return ℑ
}
return nullptr;
}
private:
};
class Transform: public Transformer {
public:
Transform(){
create_image_input("image input","Image input");
title="Transform";
description="Apply 2D transformation";
};
Transform(map<string,string> &settings):Transform() {
base_settings(settings);
};
~Transform(){
};
Transform* clone(map<string,string> &_settings) { return new Transform(_settings);};
Image *output(const Frame_spec &frame){
return transform(image_inputs[0]->get(frame));
}
private:
};
class Still_image: public Transformer {
public:
Still_image(){
create_attribute("filename","name of image file to load","File name","");
title="Still image";
description="Load a still image and apply 2D transformation";
};
Still_image(map<string,string> &settings):Still_image() {
base_settings(settings);
if (attributes["filename"]->value!=""){
string filewithpath=find_setting(settings,"media_path","")+attributes["filename"]->value;
if (still.read_file(filewithpath)) {
cerr<<"Still_image: loaded "<<filewithpath<<endl;
}
else cerr<<"Still_image: could not load "<<filewithpath<<endl;
}
};
~Still_image(){
};
Still_image* clone(map<string,string> &_settings) { return new Still_image(_settings);};
Image *output(const Frame_spec &frame){
if (!still.rgb.empty()){
return transform(&still);
}
else return nullptr;
}
private:
Image still;
};
}
#endif
|