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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
#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 Y",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"});
};
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/180)*3.1415926f;
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);
cv::Mat out_mat(3,3,CV_32FC1);
//normally a scale of 1 will place the image on screen at pixel size
//it should be that a scale of 1 places it at width w
//how to scale around the centre
//using mipmaps:
cv::Mat inter;
//int level=(int)ceil(log(1.0f/(s*((float)in.w/(float)image.w)))/log(2));
//if (s<1){
// if (s<.01) s=.01;
// float scalefac=((float)image.w/in->w)*s;
// cv::resize(in->rgb,inter,cv::Size(in->w*scalefac,in->h*scalefac),s,s); //double fx=0, double fy=0, int interpolation=INTER_LINEAR )¶
// s=1.0f;
//}
//else {
inter=in->rgb;
s=((float)image.w/in->w)*s;
//}
// 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=image.w-1;
srcTri[1].y=0;
srcTri[2].x=0;
srcTri[2].y=image.h-1;
for (int i=0;i<3;i++){
dstTri[i].x=srcTri[i].x+(tX*image.w);
dstTri[i].y=srcTri[i].y+(tY*image.w); //use w for equiv coords
//rotate and scale around centre
//transform to centre
dstTri[i].x-=(oX*image.w);
dstTri[i].y-=(oY*image.w);
dstTri[i].x*=s;
dstTri[i].y*=s;
float dx=(dstTri[i].x*cos(r))-(dstTri[i].y*sin(r));
float dy=(dstTri[i].x*sin(r))+(dstTri[i].y*cos(r));
dstTri[i].x=dx;
dstTri[i].y=dy;
//transform back
dstTri[i].x+=(oX*image.w);
dstTri[i].y+=(oY*image.w);
}
trans_mat=getAffineTransform( srcTri, dstTri );
warpAffine( in->rgb, image.rgb, trans_mat, image.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
//trans_mat.resize(3);
//rot_mat.resize(3);
//trans_mat.data[8]=1.0f;
//rot_mat.data[8]=1.0f;
//out_mat=rot_mat*trans_mat;
//out_mat.resize(2);
//warpAffine( inter, image.rgb, out_mat, image.rgb.size(), filtmode, cv::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";
UID="c798586c-2d0a-11e3-a736-6f81df06fd1b";
};
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","");
create_attribute("media_id","media_id","media_id","media_id");
title="Still image";
description="Load a still image and apply 2D transformation";
UID="d464b0d6-2d0a-11e3-acb7-6be231762445";
};
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;
};
#define MIRROR_horiz 1
#define MIRROR_vert 2
#define MIRROR_horizR 3
#define MIRROR_vertR 4
class Mirror: public Image_node {
public:
Mirror(){
create_image_input("image input","Image input");
create_attribute("mode","Mirror mode","Mirror mode","horiz",{"horiz","vert","horizR","vertR"});
title="Mirror";
description="Mirror video across a central axis";
UID="1fed5b26-2d0a-11e3-9901-4b5ea78a005d";
};
Mirror(map<string,string> &settings):Mirror() {
base_settings(settings);
};
~Mirror(){ };
Mirror* clone(map<string,string> &_settings) { return new Mirror(_settings);};
Image *output(const Frame_spec &frame){
Image *in=image_inputs[0]->get(frame);
if (in){
//copy incoming image **writable
image=(*in);
//could be more efficient here by only copying once
switch (attributes["mode"]->intVal) {
case MIRROR_horiz:
for (int i=0;i<image.w/2;i++){
for (int j=0;j<image.h;j++){
for (int k=0;k<3;k++){
image.RGBdata[(((j*image.w)+((image.w/2)+i))*3)+k]=image.RGBdata[(((j*image.w)+((image.w/2)-i))*3)+k];
}
}
}
break;
case MIRROR_vert:
for (int i=0;i<image.w;i++){
for (int j=0;j<image.h/2;j++){
for (int k=0;k<3;k++){
image.RGBdata[((((image.h/2+j)*image.w)+i)*3)+k]=image.RGBdata[((((image.h/2-j)*image.w)+i)*3)+k];
}
}
}
break;
case MIRROR_horizR:
for (int i=0;i<image.w/2;i++){
for (int j=0;j<image.h;j++){
for (int k=0;k<3;k++){
image.RGBdata[(((j*image.w)+((image.w/2)-i))*3)+k]=image.RGBdata[(((j*image.w)+((image.w/2)+i))*3)+k];
}
}
}
break;
case MIRROR_vertR:
for (int i=0;i<image.w;i++){
for (int j=0;j<image.h/2;j++){
for (int k=0;k<3;k++){
image.RGBdata[((((image.h/2-j)*image.w)+i)*3)+k]=image.RGBdata[((((image.h/2+j)*image.w)+i)*3)+k];
}
}
}
break;
}
return ℑ
}
return nullptr;
}
private:
};
}
#endif
|