Hugin trunk 0.1
Loading...
Searching...
No Matches
MeshRemapper.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
23#include "panoinc_WX.h"
24#include "panoinc.h"
25
26#include <math.h>
27#include "MeshRemapper.h"
28#include "ViewState.h"
29
38
42
44{
45 // we want to make a remapped mesh, get some generic information:
46// const HuginBase::SrcPanoImage *src = visualization_state->GetSrcImage(image_number);
47 // get the size of the image.
48 width = (double) image->getSize().width();
49 height = (double) image->getSize().height();
50
51 // use the scale to determine edge lengths in pixels for any
52 // resolution selection.
54 // It is up to the child classes to determine what to do here. They should
55 // probably use set up transform and use it to fill some data structure that
56 // stores coordinates.
57}
58
60{
61// const HuginBase::SrcPanoImage *src = visualization_state->GetSrcImage(image_number);
62 crop_mode = image->getCropMode();
63 crop_x1 = (double) image->getCropRect().left() / width;
64 crop_x2 = (double) image->getCropRect().right() / width;
65 crop_y1 = (double) image->getCropRect().top() / height;
66 crop_y2 = (double) image->getCropRect().bottom() / height;
67 // set variables for circular crop.
69 if (circle_crop)
70 {
73 double crop_width_px = (double) image->getCropRect().width(),
74 crop_hieght_px = (double) image->getCropRect().height(),
79 }
80 // hugin allows negative cropping regions, but we are expected to only
81 // output the regions that exist in the original image, so clamp the values:
82 if (crop_x1 < 0.0) crop_x1 = 0.0; if (crop_x1 > 1.0) crop_x1 = 1.0;
83 if (crop_x2 < 0.0) crop_x2 = 0.0; if (crop_x2 > 1.0) crop_x2 = 1.0;
84 if (crop_y1 < 0.0) crop_y1 = 0.0; if (crop_y1 > 1.0) crop_y1 = 1.0;
85 if (crop_y2 < 0.0) crop_y2 = 0.0; if (crop_y2 > 1.0) crop_y2 = 1.0;
86}
87
88/******************************************************************************
89 * Clipping stuff *
90 ******************************************************************************/
91
92// a vertex has vertex coordinates and a texture coordinates.
93class Vertex
94{
95public:
96 Vertex(double vx, double vy, double tx, double ty);
97 Vertex() {}
98 double vertex_c[2];
99 double tex_c[2];
100};
101
102Vertex::Vertex(double vx, double vy, double tx, double ty)
103{
104 vertex_c[0] = vx;
105 vertex_c[1] = vy;
106 tex_c[0] = tx;
107 tex_c[1] = ty;
108}
109
110// The A_Polygon class stores an arbitrary polygon, and can give you another
111// polygon that is the result of clipping it with some line.
112// hmmm... Using the name "Polygon" causes errors on windows, hence A_Polgon.
114{
115public:
116 // Create the polygon from a quadrilateral
117 explicit A_Polygon(MeshRemapper::Coords *face);
119 // clip lines are the line defined by x * p[0] + y * p[1] + p[2] = 0.
120 // inside is the side where x * p[0] + y *p[1] + p[2] > 0.
121 A_Polygon Clip(const double clip_line[3]);
122 // add a vertex on the end of the list. Used to build the polygon when not
123 // created from a quad.
124 inline void AddVertex(Vertex v);
125 // convert to a list of quads. A bit approximate.
126 std::vector<MeshRemapper::ArrayCoords> ConvertToQuads();
127private:
128 // our list of vertices
129 std::vector<Vertex> verts;
130 // Test which side of the clip plane we are on
131 inline bool Inside(const unsigned int vertex,
132 const double clip_line[3]) const;
133 // given that the edge between the specified vertices crosses the clip line,
134 // find where they cross and set the vertex and texture coordinates there.
135 Vertex Intersect(const unsigned int v1, const unsigned int v2,
136 const double clip_line[3]) const;
137};
138
140{
141 verts.resize(4);
142 for (unsigned short int v = 0; v < 4; v++)
143 {
144 // give verticies counter-clockwise
145 unsigned short int x = (v == 2 || v==3) ? 1 : 0,
146 y = (v == 1 || v==2) ? 1 : 0;
147 for (unsigned short int a = 0; a < 2; a++)
148 {
149 verts[v].vertex_c[a] = face->vertex_c[x][y][a];
150 verts[v].tex_c[a] = face->tex_c[x][y][a];
151 }
152 }
153}
154
156{
157 verts.push_back(v);
158}
159
160inline bool A_Polygon::Inside(const unsigned int v, const double l[3]) const
161{
162 return verts[v].tex_c[0] * l[0] + verts[v].tex_c[1] * l[1] + l[2] > 0;
163}
164
166 const unsigned int v2_index,
167 const double cl[3]) const
168{
169 // find the point of intersection from the given edge with a clip plane.
170 // the edge is the vertex with the same number and the one before it.
171 // Get pointers to the vertecies we will use
172 const Vertex *v1 = &verts[v1_index],
173 // the vertex before is the last one if we at the beginning.
174 *v2 = &verts[v2_index];
175 // find a vector along the path of the edge we are clipping
176 double dx = v2->tex_c[0] - v1->tex_c[0],
177 dy = v2->tex_c[1] - v1->tex_c[1];
178 // The line along the edge we clip is defined by (x,y) = v1 + t(dx, dy)
179 // The line of the clipping plane is cl[0] * x + cl[1] * y + cl[2] = 0
180 // now find what value of t that is on both lines.
181 // substitute x and y from the edge line into the equation of the clip line:
182 // cl[0] * (v1_x + t * dx) + cl[1] * (v1_y + t * dy) + cl[2] = 0
183 // then rearrange to get t:
184 // cl[0] * v1_x + t * cl[0] * dx + cl[1] * v1_y + t * cl[1] * dy + cl[2] = 0
185 // t * (cl[0] * dx + cl[1] * dy) + cl[0] * v1_x + cl[1] * v1_y + cl[2] = 0
186 // t * (cl[0] * dx + cl[1] * dy) = -cl[0] * v1_x - cl[1] * v1_y - cl[2]
187 // so we get:
188 /* FIXME this assertion sometimes fails. t should always be between 0 and 1,
189 * but sometimes isn't even when this one passes:
190 */
191 // DEBUG_ASSERT(cl[0] * dx + cl[1] * dy);
192 double t = (-cl[0] * v1->tex_c[0] - cl[1] * v1->tex_c[1] - cl[2]) /
193 (cl[0] * dx + cl[1] * dy),
194 // now substitute t into the edge we are testing's line:
195 x = dx * t + v1->tex_c[0],
196 y = dy * t + v1->tex_c[1],
197 // move the vertex coordinates to match the texture ones.
198 // t = 0 would give v1, t = 1 would give v2; so we linearly interpolate by t:
199 td1 = 1.0 - t,
200 xc = v1->vertex_c[0] * td1 + v2->vertex_c[0] * t,
201 yc = v1->vertex_c[1] * td1 + v2->vertex_c[1] * t;
202 // DEBUG_ASSERT(-0.1 <= t && t <= 1.1);
203 return Vertex(xc, yc, x, y);
204}
205
207{
208 // We'll use the Sutherland-Hodgman clipping algorithm.
209 // see http://en.wikipedia.org/wiki/Sutherland-Hodgeman
210 A_Polygon result;
211 unsigned int vertices_count = verts.size(),
213 // we want to examine all edges in turn, and find the vertices to keep:
214 for (unsigned int v_index = 0; v_index < vertices_count; v_index++)
215 {
219 {
220 // one in, one out, therefore the edge instersts the clip line.
222 }
223 if (v_index_inside)
224 {
225 // keep any inner vertices.
226 result.AddVertex(verts[v_index]);
227 }
229 }
230 return result;
231}
232
233std::vector<MeshRemapper::ArrayCoords> A_Polygon::ConvertToQuads()
234{
235 // we want to convert any old polygon to quadrilaterals.
236 /* If the input was from a TexCoordRemapper, we'll have a rectangle with
237 * some edges chopped off, this could be empty, or a convex polygon.
238 * If we got our input from a VertexCoordRemapper it is likely to be (but
239 * not necessarily) convex.
240 * This is only guaranteed to work on convex polygons, though it will work
241 * some concave ones.
242 * FIXME Either use something more stable for concave polygons, or allow
243 * the Coords structure to take arbitrary polygons.
244 * Warning: The output of the clipping algorithm can create coincident lines
245 * where it has clipped through something concave. See this:
246 * http://en.wikipedia.org/wiki/Sutherland-Hodgman_clipping_algorithm
247 * It would be fine to leave those in if we don't mess up converting it to
248 * quadrilaterals (for example if sending the polygon directly to OpenGL)
249 */
250 std::vector<MeshRemapper::ArrayCoords> result;
251 unsigned int vertices_count = verts.size();
252 unsigned int i = 2;
253 // use vertex 0 for each face, and share another vertex with the last drawn,
254 // to (at least in convex cases) get the shape we intended, like this:
255 /* 0+------,7
256 * /|\ c \
257 * 1 / | \ \ 6
258 * |a | \ |
259 * | | \|
260 * 2 \ | b / 5
261 * \| /
262 * 3+------'4
263 */
264 while (i < vertices_count)
265 {
266 if (i < vertices_count - 1)
267 {
268 // make a new quadrilateral
270 for (unsigned short int c = 0; c < 2; c++)
271 {
272 quad.vertex_c[0][0][c] = verts[ 0 ].vertex_c[c];
273 quad.tex_c [0][0][c] = verts[ 0 ].tex_c [c];
274 quad.vertex_c[0][1][c] = verts[i - 1].vertex_c[c];
275 quad.tex_c [0][1][c] = verts[i - 1].tex_c [c];
276 quad.vertex_c[1][1][c] = verts[ i ].vertex_c[c];
277 quad.tex_c [1][1][c] = verts[ i ].tex_c [c];
278 quad.vertex_c[1][0][c] = verts[i + 1].vertex_c[c];
279 quad.tex_c [1][0][c] = verts[i + 1].tex_c [c];
280 }
281 result.push_back(quad);
282 i += 2;
283 }
284 else
285 {
286 // make a new triangle, but repeat the last vertex so it is a quad
288 for (unsigned short int c = 0; c < 2; c++)
289 {
290 quad.vertex_c[0][0][c] = verts[ 0 ].vertex_c[c];
291 quad.tex_c [0][0][c] = verts[ 0 ].tex_c [c];
292 quad.vertex_c[0][1][c] = verts[i - 1].vertex_c[c];
293 quad.tex_c [0][1][c] = verts[i - 1].tex_c [c];
294 quad.vertex_c[1][1][c] = verts[ i ].vertex_c[c];
295 quad.tex_c [1][1][c] = verts[ i ].tex_c [c];
296 quad.vertex_c[1][0][c] = verts[ i ].vertex_c[c];
297 quad.tex_c [1][0][c] = verts[ i ].tex_c [c];
298 }
299 result.push_back(quad);
300 i ++;
301 }
302 }
303 return result;
304}
305
307{
308 // Clip the given face so that it only shows the cropped region of the
309 // source image.
310 // We use the texture coordinates to work out where to crop, and adjust the
311 // vertices locations accordingly.
312
313 // convert the face to a polygon:
314 A_Polygon poly(face);
315 // work out where the clipping lines are, we use the cropping rectangle:
316 double clip_lines[4][3] = {{ 1.0, 0.0, -crop_x1},
317 {-1.0, 0.0, crop_x2},
318 { 0.0, 1.0, -crop_y1},
319 { 0.0, -1.0, crop_y2}};
320 // cut off each side
321 poly = poly.Clip(clip_lines[0]);
322 poly = poly.Clip(clip_lines[1]);
323 poly = poly.Clip(clip_lines[2]);
324 poly = poly.Clip(clip_lines[3]);
325 // Feign circular clipping by trimming to some tangent lines.
326 /* Work out what angles to clip to based on the locations of the vertices.
327 * This is generally good, although it will break down where {a huge part /
328 * all / even more} of the image contained within the input face. However in
329 * this case the interpolation would make it well out of sync anyway, so
330 * it's no big deal.
331 */
332 /* Since the coordinates are scaled to between 0 and 1, we actually need to
333 * crop to a elipse to get the aspect ratio right.
334 */
335 if (circle_crop)
336 {
337 for (unsigned int edge = 0; edge < 4; edge++)
338 {
339 double angle = M_PI + atan2(face->tex_c[edge % 2][edge / 2][1]
341 face->tex_c[edge % 2][edge / 2][0]
347 poly = poly.Clip(clip_line);
348 }
349 }
350 // now convert to quadrilaterals.
351 face_list = poly.ConvertToQuads();
352}
353
355{
356 if (face_list.empty())
357 {
358 // no more faces
359 return false;
360 } else {
361 // return a face
362 for (unsigned short int x = 0; x < 2; x++)
363 {
364 for (unsigned short int y = 0; y < 2; y++)
365 {
366 for (unsigned short int c = 0; c < 2; c++)
367 {
368 result->tex_c[x][y][c] = face_list.back().tex_c[x][y][c];
369 result->vertex_c[x][y][c] = face_list.back().vertex_c[x][y][c];
370 }
371 }
372 }
373 face_list.pop_back();
374 return true;
375 }
376}
377
#define M_PI
void AddVertex(Vertex v)
bool Inside(const unsigned int vertex, const double clip_line[3]) const
std::vector< Vertex > verts
A_Polygon Clip(const double clip_line[3])
Vertex Intersect(const unsigned int v1, const unsigned int v2, const double clip_line[3]) const
std::vector< MeshRemapper::ArrayCoords > ConvertToQuads()
Model for a panorama.
Definition Panorama.h:153
All variables of a source image.
A class for exchanging coordinates by value.
double vertex_c[2][2][2]
A class for exchanging pointers to coordinates.
double(* vertex_c)[2][2]
The coordinate in the panorama, in its pixel space.
double(* tex_c)[2][2]
The coordinate in the source image ranging from 0 to 1.
double circle_crop_radius_y
HuginBase::SrcPanoImage::CropMode crop_mode
Crop mode of the source image.
HuginBase::SrcPanoImage * image
void ClipFace(Coords *face)
Crop a face to the source image, return true if there is anything left.
bool GiveClipFaceResult(Coords *result)
Get a face that was produced by ClipFace.
double height
The sizes of the input images in pixels.
float scale
The number number of units between vertex coorinates that gives a pixel in the display.
MeshRemapper(HuginBase::Panorama *m_pano, HuginBase::SrcPanoImage *image, VisualizationState *visualization_state)
double circle_crop_centre_y
double circle_crop_radius_x
virtual void UpdateAndResetIndex()
std::vector< MeshRemapper::ArrayCoords > face_list
List for results when clipping faces.
virtual ~MeshRemapper()
VisualizationState * visualization_state
double circle_crop_centre_x
void SetCrop()
Fill the crop values of the MeshRemapper from the source image.
HuginBase::Panorama * m_pano
double tex_c[2]
double vertex_c[2]
include file for the hugin project
include file for the hugin project
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41