summaryrefslogtreecommitdiff
path: root/rotord/src/nodes_transform.h
blob: d54b683c525551c899e621dd470f0d0ee7abc7ac (plain)
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
#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"});
			};
			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
						cv::Mat inter;
						//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 &image;
	                }
				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