Hugin trunk 0.1
Loading...
Searching...
No Matches
PreviewLayoutLinesTool.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
22#include "panoinc_WX.h"
23#include "panoinc.h"
24
26#include "hugin_config.h"
27
28#include "base_wx/platform.h"
29#include "MainFrame.h"
30#include "GreatCircles.h"
31
32#include <wx/platform.h>
33#ifdef __WXMAC__
34#include <OpenGL/gl.h>
35#include <OpenGL/glu.h>
36#else
37#include <GL/gl.h>
38#include <GL/glu.h>
39#endif
40
41#include <cfloat>
42#include <cmath>
43
44// the size of the rectangular texture. Must be a power of two, and at least 8.
45#define rect_ts 64
46
47// The resolution to use to sample overlaps.
48/* The time it takes to sample it is a multiple of this squared + a constant.
49 * So keep it low to make the redraws faster, set it higher to make the grey
50 * lines' appearence more accurate. Make sure to adjust MIN_SAMPLE_OVERLAPS
51 * below to keep the proportion that need to overlap the same.
52 */
53#define SAMPLE_FREQUENCY 12
54
55/* The amount of samples that are required to be within the other image for it
56 * to count as a large enough overlap to warrent a grey line. It is affected by
57 * the SAMPLE_FREQUENCY. An image with SAMPLE_FREQUENCY squared overlaps is
58 * entirely within the other image. Must be > 0.
59 *
60 * 12*12 = 144 samples. 16 / 144 = 1/9, so a 9th of the image must overlap for
61 * the grey line to appear. If just the corners overlap, the overlap must be a
62 * third in both directions:
63 * |||
64 * |||
65 * ||$SS
66 * SSS
67 * SSS
68 */
69#define MIN_SAMPLE_OVERLAPS 16
70
72class PosMap
73{
74public:
76 hugin_utils::FDiff2D * operator[](std::size_t index)
77 {
78 return data[index];
79 }
80};
81
83 : Tool(helper),
84 m_holdOnNear(false),
85 m_updateStatistics(true),
86 m_nearestLine(-1),
87 m_useNearestLine(false)
88{
90 // make the textures. We have a circle border and a square one.
91 // the textures are white with a the alpha chanel forming a border.
93 // we only want to specify alpha, but using just alpha in opengl attaches 0
94 // for the luminosity. I tried biasing the red green and blue values to get
95 // them to 1.0, but it didn't work under OS X for some reason. Instead we
96 // use a luminance alpha pair, and use 1.0 for luminance all the time.
97
98 // In the rectangle texture, the middle is 1/8 opaque, the outer pixels
99 // are completely transparent, and one pixel in from the edges is
100 // a completly opaque line.
102 // make everything white
103 for (unsigned int x = 0; x < rect_ts; x++)
104 {
105 for (unsigned int y = 0; y < rect_ts; y++)
106 {
107 rect_tex_data[x][y][0] = 255;
108 }
109 }
110 // now set the middle of the mask semi transparent
111 for (unsigned int x = 2; x < rect_ts - 2; x++)
112 {
113 for (unsigned int y = 2; y < rect_ts - 2; y++)
114 {
115 rect_tex_data[x][y][1] = 31;
116 }
117 }
118 // make an opaque border
119 for (unsigned int d = 1; d < rect_ts - 1; d++)
120 {
121 rect_tex_data[d][1][1] = 255;
122 rect_tex_data[d][rect_ts - 2][1] = 255;
123 rect_tex_data[1][d][1] = 255;
124 rect_tex_data[rect_ts - 2][d][1] = 255;
125 }
126 // make a transparent border around that
127 for (unsigned int d = 0; d < rect_ts; d++)
128 {
129 rect_tex_data[d][0][1] = 0;
130 rect_tex_data[d][rect_ts - 1][1] = 0;
131 rect_tex_data[0][d][1] = 0;
132 rect_tex_data[rect_ts - 1][d][1] = 0;
133 }
139 // clamp texture so it won't wrap over the border of the cropped region.
142}
144{
145 // free the textures
147
148 // delete all the transforms.
149 while (!m_transforms.empty())
150 {
151 delete m_transforms.back();
152 m_transforms.pop_back();
153 }
154
155 // stop observing the panorama
157}
158
168
170{
171 // register notifications
176
177 helper->SetStatusMessage(_("Click a connection to edit control points."));
178}
179
181{
182
183 // Try to find the nearest line to the mouse pointer.
184 // ...Unless there are no lines.
185 if (m_lines.empty())
186 {
187 return;
188 }
189
190 if (!(helper->IsMouseOverPano())) {
191 if (m_useNearestLine) {
192 m_useNearestLine = false;
195 }
196 return;
197 }
198
199 if (e.Dragging() && !m_holdOnNear) {
200 return;
201 }
202
203 // Check each line in turn.
204 double minDistance = DBL_MAX;
205 unsigned int nearestLineOld = m_nearestLine;
206 for (unsigned int i = 0; i < m_lines.size(); i++)
207 {
208 if (m_lines[i].dud) continue;
209 double lineDistance = m_lines[i].getDistance(helper->GetMousePanoPosition());
211 {
212 // found a new minimum.
215 }
216 }
217
218 // Work out if it is close enough to highlight it.
220 // The limit is 70 pixels from the line.
221 // Coordinates are panorama pixels squared, so we'll need to scale it.
222 double scale = helper->GetVisualizationStatePtr()->GetScale();
223 scale *= scale;
224 m_useNearestLine = minDistance < 4900.0 / scale;
225
227 {
229 // get notification of when the connected images are drawn so we can
230 // draw them on top with a highlight.
233
234 // Redraw with new indicators. Since the indicators aren't part of the
235 // panorama, we have to persuade the viewstate that a redraw is required.
238 }
239}
240
242{
243 // We should check if the lines or image centers have changed.
245 {
246 m_updateStatistics = false;
247 // Must be done in this order since the transforms updateImageCentres
248 // uses are also used by updateLineInformation.
251
252 if (m_nearestLine >= m_lines.size())
253 {
254 // The line we had selected no longer exists. Use the first line
255 // until the mouse moves again.
256 m_nearestLine = 0;
257 }
258 }
259
260 // now draw each line.
262 helper->GetViewStatePtr()->GetTextureManager()->DisableTexture();// for (unsigned int i = 0; i < m_lines.size(); i++)
263 for (unsigned int i = 0; i < m_lines.size(); i++)
264 {
266 }
269 // reset some openGL state back to its normal value.
270 glColor3ub(255, 255, 255);
271 glLineWidth(1.0);
272}
273
275{
276 // we draw the partly transparent identification boxes over the top of the
277 // two images connecting the nearest line to the mouse pointer.
278
279 // If there are no lines, there isn't a nearest one. Also check if we use it
280 if (m_lines.empty() || !m_useNearestLine) return;
281
282 // draw the actual images
283 unsigned int image1 = m_lines[m_nearestLine].image1;
284 unsigned int image2 = m_lines[m_nearestLine].image2;
289
290 // Setup OpenGL blending state for identification borders.
293 // use the border texture.
296 // We use the texture matrix to align the texture with the cropping region.
298
299 // now draw the identification boxes
302
303 // set OpenGL state back how we found it.
306}
307
309{
310 // we want to shift the texture so it lines up with the cropped region.
311 glPushMatrix();
313 GetSrcImage(image));
314 int width = src.getSize().width(), height = src.getSize().height();
315 vigra::Rect2D crop_region = src.getCropRect();
316 // pick a texture depending on crop mode and move it to the cropped area
317 switch (src.getCropMode())
318 {
321 // get the biggest rectangle contained by both the image and the
322 // cropped area.
323 crop_region &= vigra::Rect2D(src.getSize());
324 glScalef((float) width / (float) crop_region.width(),
325 (float) height / (float) crop_region.height(),
326 1.0);
327 glTranslatef(-(float) crop_region.left() / (float) width,
328 -(float) crop_region.top() / (float) height,
329 0.0);
330 break;
332 break;
333 }
334 // draw the image with the border texture.
338 // reset the texture matrix.
339 glPopMatrix();
340}
341
343{
344 if (m_lines.empty() || !m_useNearestLine)
345 {
346 // there are no lines, so none should be highlighted, therefore every
347 // image must be drawn. Don't notify us any longer and draw the image.
349 return true;
350 }
351 // Delay drawing of images, so we can show them on top of the others.
353 if (image == line.image1 || image == line.image2) return false;
354 // We must be done with this event, so don't notify us any longer:
356 return true;
357}
358
360{
361 // If it was a left click and we have at least one line, bring up the images
362 // in that line in the control point editor.
363 if ( e.LeftDown() && !m_lines.empty() && m_useNearestLine)
364 {
365 m_holdOnNear = true;
366 }
367
368 if (m_holdOnNear && e.LeftUp() && m_useNearestLine) {
369 m_holdOnNear = false;
372 MainFrame::Get()->Raise();
373 }
374
375 if (m_useNearestLine && e.LeftUp()) {
376 m_useNearestLine = false;
377 }
378}
379
381{
382 m_lines.clear();
383 const HuginBase::Panorama & pano = *(helper->GetPanoramaPtr());
384 unsigned int numberOfImages = pano.getNrOfImages();
386 // make a line for every image pair, but set the unneeded ones as dud.
387 // This is for constant look up times when we scan control points.
389 unsigned int numberOfControlPoints = pano.getNrOfCtrlPoints();
390 // loop over all control points to count them and get error statistics.
391 for (unsigned int cpi = 0 ; cpi < numberOfControlPoints ; cpi++)
392 {
395 {
396 // don't count line control points
397 continue;
398 };
399 unsigned int low_index, high_index;
400 if (cp.image1Nr < cp.image2Nr)
401 {
403 high_index = cp.image2Nr;
404 } else {
405 low_index = cp.image2Nr;
406 high_index = cp.image1Nr;
407 }
408 // find the line.
409 // We use the formula in the line below to record image numbers to each
410 // line later.
412 // update control point count.
414 // update error statistics
415 line.totalError += cp.error;
416 if (cp.error > line.worstError)
417 {
418 line.worstError = cp.error;
419 }
420 }
421
422 /* Find some locations of the images. We will test if they overlap using
423 * these locations. We don't need the last image as we can always take the
424 * smallest numbered image as the source of points.
425 */
432 std::vector<PosMap> positions(pano.getNrOfImages() - 1);
433 for (unsigned int i = 0; i < numberOfImages - 1; i++)
434 {
435 const HuginBase::SrcPanoImage & img = pano.getImage(i);
436 for (unsigned int x = 0; x < SAMPLE_FREQUENCY; x++)
437 {
438 for (unsigned int y = 0; y < SAMPLE_FREQUENCY; y++)
439 {
440 // scale (x, y) so it is always within the cropped region of the
441 // image.
442 vigra::Rect2D c = img.getCropRect();
446 double xc = double (x) / double (SAMPLE_FREQUENCY)
447 * double(c.width()) + c.left();
448 double yc = double (y) / double (SAMPLE_FREQUENCY)
449 * double(c.height()) + c.top();
450 // now look up (xc, yc) in the image, find where in the panorama
451 // it ends up.
452 m_transforms[i]->transformImgCoord (
453 positions[i][x][y].x, positions[i][x][y].y,
454 xc, yc );
455 }
456 }
457 }
458
459 // write other line data.
460 for (unsigned int i = 0; i < numberOfImages; i++)
461 {
462 for (unsigned int j = 0; j < numberOfImages; j++)
463 {
464 LineDetails & line = m_lines[i * numberOfImages + j];
465 line.image1 = i;
466 line.image2 = j;
468 if (!(set_contains(active_images, i) &&
470 {
471 // At least one of the images is hidden, so don't show the line.
472 line.dud = true;
473 }
474 else if (line.numberOfControlPoints > 0)
475 {
476 line.dud = false;
477 } else if (i >= j) {
478 // We only use lines where image1 is the lowest numbered image.
479 // We don't bother with lines from one image to the same one.
480 line.dud = true;
481 } else {
482 // test overlapping regions.
485 HuginBase::SrcPanoImage & src = *viewState.GetSrcImage(j);
486 transform.createTransform(src, *(viewState.GetOptions()));
487 unsigned int overlapingSamples = 0;
488 for (unsigned int x = 0; x < SAMPLE_FREQUENCY; x++)
489 {
490 for (unsigned int y = 0; y < SAMPLE_FREQUENCY; y++)
491 {
492 // check if mapping a point that was found earilier to
493 // be inside an image in panorama space is inside the
494 // other image when transformed from panorama to image.
495 double dx, dy;
496 transform.transformImgCoord (
497 dx, dy,
498 positions[i][x][y].x,
499 positions[i][x][y].y
500 );
501 if (src.isInside(vigra::Point2D((int) dx, (int) dy)))
502 {
503 // they overlap
505 }
506 }
507 }
508 // If the overlap isn't big enough, the line isn't used.
510 }
511
512 if (!line.dud)
513 {
519 }
520 }
521 }
522}
523
525{
527 // The transforms have no copy constructor, so we can't have a direct vector
528 // and use resize. Instead we have a vector of pointers and we create and
529 // delete transforms as they are needed.
530 while (m_transforms.size() > numberOfImages)
531 {
532 delete m_transforms.back();
533 m_transforms.pop_back();
534 }
536 while (m_transforms.size() < numberOfImages)
537 {
539 }
542
545 spherical_pano_opts.setWidth(360);
546 spherical_pano_opts.setHeight(180);
547 spherical_pano_opts.setHFOV(360);
548
549 for (unsigned int image_number = 0;
550 image_number < helper->GetPanoramaPtr()->getNrOfImages();
551 image_number++)
552 {
553 // transforming image coordinates to panorama coordinates.
554 m_transforms[image_number]->createInvTransform (
555 *(helper->GetViewStatePtr()->GetSrcImage(image_number)),
557 );
560 *(helper->GetViewStatePtr()->GetSrcImage(image_number)),
562 );
563 const vigra::Size2D & s = helper->GetViewStatePtr()->GetSrcImage(image_number)->getSize();
564 // find where the middle of the image maps to.
565 m_transforms[image_number]->transformImgCoord (
566 m_imageCentres[image_number].x,
567 m_imageCentres[image_number].y,
568 double(s.x) / 2.0, double(s.y) / 2.0
569 );
570 to_spherical.transformImgCoord(
571 m_imageCentresSpherical[image_number].x,
572 m_imageCentresSpherical[image_number].y,
573 double(s.x) / 2.0, double(s.y) / 2.0
574 );
575 }
576}
577
579{
580 if (dud)
581 {
582 // The line isn't real, don't draw it.
583 return;
584 }
585 // work out the colour
586 if (highlight)
587 {
588 glColor3ub(255, 255, 255);
589 } else if (numberOfControlPoints == 0) {
590 // no control points, use a grey line
591 glColor3ub(170, 170, 170);
592 }else {
593 double red, green, blue;
596 }
597
598 double lineWidth = numberOfControlPoints / 5.0 + 1.0;
599 if (lineWidth > 5.0) lineWidth = 5.0;
600// glLineWidth(lineWidth);
601
602 arc.draw(false, lineWidth);
603}
604
606 : numberOfControlPoints(0),
607 worstError(0),
608 totalError(0)
609{
610
611}
612
614{
615 if (dud)
616 {
617 // This isn't a real line, so return the maximum distance to it possible.
618 return FLT_MAX;
619 }
620 else
621 {
622 return arc.squareDistance(point);
623 }
624}
625
Declare GreatCircles class.
#define rect_ts
#define MIN_SAMPLE_OVERLAPS
#define SAMPLE_FREQUENCY
void draw(bool withCross=true, double width=1.0) const
Draw the great circle arc on the fast preview.
represents a control point
Holds transformations for Image -> Pano and the other way.
bool transformImgCoord(double &x_dest, double &y_dest, double x_src, double y_src) const
like transform, but return image coordinates, not cartesian coordinates
void createTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
initialize pano->image transformation
void createInvTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
create image->pano transformation
Panorama image options.
void setProjection(ProjectionFormat f)
set the Projection format and adjust the hfov/vfov if nessecary
Model for a panorama.
Definition Panorama.h:153
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
void addObserver(PanoramaObserver *o)
add a panorama observer.
const ControlPoint & getCtrlPoint(std::size_t nr) const
get a control point, counting starts with 0
Definition Panorama.h:312
std::size_t getNrOfCtrlPoints() const
number of control points
Definition Panorama.h:306
bool removeObserver(PanoramaObserver *observer)
remove a panorama observer.
UIntSet getActiveImages() const
get active images
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
static MainFrame * Get()
hack.. kind of a pseudo singleton...
void ShowCtrlPointEditor(unsigned int img1, unsigned int img2)
opens the control points tab with the both images selected
A map from image positions to panorama positions.
hugin_utils::FDiff2D * operator[](std::size_t index)
hugin_utils::FDiff2D data[SAMPLE_FREQUENCY][SAMPLE_FREQUENCY]
A class to store information about each line that will be drawn.
double totalError
the total of all the control point errors between these images
double worstError
the error of the control point with the greatest error between these images
float getDistance(hugin_utils::FDiff2D point)
Get the square of the distance from the arc to some panorama coordinate.
bool dud
false if the line should be used, true if it shouldn't.
unsigned int image1
index of images for this line
void draw(bool highlight)
Draw a line in the preview for this pair of images.
unsigned int numberOfControlPoints
the number of control points between these images
unsigned int m_nearestLine
The index of nearest line to the mouse position.
void AfterDrawImagesEvent()
Draw a border over the images when a line is hilighted, similar to the identify tool.
void panoramaImagesChanged(HuginBase::Panorama &, const HuginBase::UIntSet &)
notifies about changes to images
void updateImageCentres()
Update the locations of the image centres.
void BeforeDrawImagesEvent()
Draw all the lines between images that indicate the quality and quantity of control points.
std::vector< HuginBase::PTools::Transform * > m_transforms
The transformations used to make the image centres.
std::vector< hugin_utils::FDiff2D > m_imageCentresSpherical
The spherical coordinates of the middle of the images.
PreviewLayoutLinesTool(ToolHelper *helper)
void Activate()
Start using the PreviewLayoutLinesTool.
void MouseButtonEvent(wxMouseEvent &e)
Capture clicks on lines, and load up the relavent images in the control point tab.
bool m_updateStatistics
Flag set to true to update statistics next redraw.
std::vector< hugin_utils::FDiff2D > m_imageCentres
The location on the panorama that the middle of the images map to.
void drawIdentificationBorder(unsigned int image)
Draw a border for an image.
void updateLineInformation()
Update the line information.
virtual void MouseMoveEvent(double x, double y, wxMouseEvent &e)
Revaluate the lines under the mouse pointer when it moves.
std::vector< LineDetails > m_lines
A container for the line information.
void panoramaChanged(HuginBase::Panorama &pano)
This just sets a flag when the panorama is changed.
bool m_useNearestLine
True if we should highlight the nearest line to the mouse position.
bool BeforeDrawImageEvent(unsigned int image)
Draw what the tool requires just before a given image is drawn.
unsigned int m_rectangleBorderTex
OpenGL texture names for the border highlight.
void DrawImage(unsigned int image_number, unsigned int display_list)
void DisableTexture(bool maskOnly=false)
hugin_utils::FDiff2D GetMousePanoPosition()
bool IsMouseOverPano()
Definition ToolHelper.h:136
HuginBase::Panorama * GetPanoramaPtr()
void NotifyMe(Event event, Tool *tool)
ViewState * GetViewStatePtr()
VisualizationState * GetVisualizationStatePtr()
void NotifyMeBeforeDrawing(unsigned int image_nr, Tool *tool)
void DoNotNotifyMeBeforeDrawing(unsigned int image_nr, Tool *tool)
@ DRAW_UNDER_IMAGES
Definition ToolHelper.h:82
@ DRAW_OVER_IMAGES
Definition ToolHelper.h:82
void SetStatusMessage(wxString message)
Definition Tool.h:43
ToolHelper * helper
The PreviewToolHelper that uses the same preview window and panorama as the tool should.
Definition Tool.h:109
HuginBase::PanoramaOptions * GetOptions()
HuginBase::SrcPanoImage * GetSrcImage(unsigned int image_nr)
TextureManager * GetTextureManager()
Definition ViewState.h:104
unsigned int GetMeshDisplayList(unsigned int image_nr)
implementation of huginApp Class
std::set< unsigned int > UIntSet
void ControlPointErrorColour(const double cperr, double &r, double &g, double &b)
Definition utils.cpp:341
include file for the hugin project
include file for the hugin project
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition stl_utils.h:74
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