Hugin trunk 0.1
Loading...
Searching...
No Matches
PreviewIdentifyTool.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
22#include "panoinc_WX.h"
23#include "panoinc.h"
24
25#include "PreviewIdentifyTool.h"
26#include "hugin_config.h"
27
28#include "base_wx/platform.h"
29#include "GLPreviewFrame.h"
30#include "MainFrame.h"
31
32#include <wx/platform.h>
33
34//multitexture feature requires glew on some systems
35#if defined HAVE_EPOXY && HAVE_EPOXY
36#include <epoxy/gl.h>
37#else
38#include <GL/glew.h>
39#endif
40
41#ifdef __WXMAC__
42#include <OpenGL/gl.h>
43#include <OpenGL/glu.h>
44#else
45#include <GL/gl.h>
46#include <GL/glu.h>
47#endif
48
49#include <algorithm>
50#include <vector>
51
52// the size of the rectangular texture. Must be a power of two, and at least 8.
53#define rect_ts 64
54// the number of times larger the circular texture is, must be a power of 2, and
55// at least 1. Making better improves the appearance of the circle.
56#define circle_ts_multiple 4
57#define circle_ts (rect_ts * circle_ts_multiple)
58#define circle_middle ((float) (circle_ts - 1) / 2.0)
59#define circle_border_outer (circle_middle - 0.5 * (float) circle_ts_multiple)
60#define circle_border_inner (circle_middle - 2.5 * (float) circle_ts_multiple)
61#define circle_border_peak (circle_middle - 1.5 * (float) circle_ts_multiple)
62
69
70#define FONT_TEXTURE_HEIGHT 75
72{
74 wxMemoryDC dc(bitmap);
75 dc.SetBackground(*wxBLACK_BRUSH);
76 dc.Clear();
78 dc.SetFont(font);
79 dc.SetTextForeground(*wxWHITE);
80 dc.DrawText("0123456789", 0, 0);
81 textureWidth = 0;
82 glyphWidth.resize(10, 0);
83 for (int i = 0; i < 10; ++i)
84 {
86 number << i;
87 wxSize textSize = dc.GetTextExtent(number);
88 textureWidth += textSize.GetWidth();
89 glyphWidth[i] = textSize.GetWidth();
90 };
91 dc.SelectObject(wxNullBitmap);
92 return bitmap;
93}
94
96 : Tool(helper)
97{
98 m_holdControl = false;
99 m_constantOn = false;
100 m_holdLeft = false;
101 m_stopUpdating = true;
104 if (!texture_created) {
105 // make the textures. We have a circle border and a square one.
106 // the textures are white with a the alpha chanel forming a border.
109 // we only want to specify alpha, but using just alpha in opengl attaches 0
110 // for the luminosity. I tried biasing the red green and blue values to get
111 // them to 1.0, but it didn't work under OS X for some reason. Instead we
112 // use a luminance alpha pair, and use 1.0 for luminance all the time.
113 {
114 // In the rectangle texture, the middle is 1/4 opaque, the outer pixels
115 // are completely transparent, and one pixel in from the edges is
116 // a completly opaque line.
118 // make everything white
119 for (unsigned int x = 0; x < rect_ts; x++)
120 {
121 for (unsigned int y = 0; y < rect_ts; y++)
122 {
123 rect_tex_data[x][y][0] = 255;
124 }
125 }
126 // now set the middle of the mask semi transparent
127 for (unsigned int x = 2; x < rect_ts - 2; x++)
128 {
129 for (unsigned int y = 2; y < rect_ts - 2; y++)
130 {
131 rect_tex_data[x][y][1] = 63;
132 }
133 }
134 // make an opaque border
135 for (unsigned int d = 1; d < rect_ts - 1; d++)
136 {
137 rect_tex_data[d][1][1] = 255;
138 rect_tex_data[d][rect_ts - 2][1] = 255;
139 rect_tex_data[1][d][1] = 255;
140 rect_tex_data[rect_ts - 2][d][1] = 255;
141 }
142 // make a transparent border around that
143 for (unsigned int d = 0; d < rect_ts; d++)
144 {
145 rect_tex_data[d][0][1] = 0;
146 rect_tex_data[d][rect_ts - 1][1] = 0;
147 rect_tex_data[0][d][1] = 0;
148 rect_tex_data[rect_ts - 1][d][1] = 0;
149 }
155 // clamp texture so it won't wrap over the border.
158 }
159 {
160 // the circular one should look similar to the rectangle one, but we
161 // enlarge it so that the circle apears less blocky. We don't want to
162 // make it equally sharper however, so we make it a bit fuzzier by
163 // blending.
165 for (unsigned int x = 0; x < circle_ts; x++)
166 {
167 for (unsigned int y = 0; y < circle_ts; y++)
168 {
169 float x_offs = (float) x - circle_middle,
170 y_offs = (float) y - circle_middle,
171 radius = sqrt(x_offs * x_offs + y_offs * y_offs),
172 intensity;
173 if (radius < circle_border_inner)
174 {
175 intensity = 63.0;
176 } else if (radius < circle_border_peak)
177 {
178 intensity = (radius - circle_border_inner) /
179 (float) circle_ts_multiple * 255.0 * 3.0 / 4.0 + 64.0;
180 } else if (radius < circle_border_outer)
181 {
182 intensity = (radius - circle_border_peak) /
183 (float) circle_ts_multiple * -255.0 + 256.0;
184 } else
185 {
186 intensity = 0.0;
187 }
188 circle_tex_data[x][y][0] = 255;
189 circle_tex_data[x][y][1] = (unsigned char) intensity;
190 }
191 }
198 // clamp texture so it won't wrap over the border.
201 }
202 {
203 int textureWidth;
205 wxImage image = fontTexture.ConvertToImage();
209 for (size_t x = 0; x < textureWidth; ++x)
210 {
211 for (size_t y = 0; y < FONT_TEXTURE_HEIGHT; ++y)
212 {
213 const unsigned char value = image.GetRed(x, y);
214 font_tex_data[2 * y * textureWidth + 2 * x] = value;
215 font_tex_data[2 * y * textureWidth + 2 * x + 1] = value;
216 };
217 };
224 delete[] font_tex_data;
225
226 font_list = glGenLists(10);
227 float posX = 0;
228 for (int loop = 0; loop<10; loop++)
229 {
232 glTexCoord2f(posX, 0);
233 glVertex2i(0, 0);
234 glTexCoord2f(posX + m_glyphWidth[loop] / static_cast<float>(textureWidth), 0);
236 glTexCoord2f(posX + m_glyphWidth[loop] / static_cast<float>(textureWidth), 1);
238 glTexCoord2f(posX, 1);
240 glEnd();
242 glEndList();
243 posX += m_glyphWidth[loop] / static_cast<float>(textureWidth);
244 }
245 }
246 texture_created = true;
247 }
248}
261
263{
264 // register notifications
269
270 // clear up
271 // Assume that there are no images under the mouse when the tool is
272 // activated. This should be fine if the user clicks the button to activate
273 // the tool.
274 m_image_set.clear();
276 /* TODO if it becomes possible to activate the tool by a keyboard shortcut
277 * or something, call ImagesUnderMouseChangedEvent() to make sure we display
278 * indicators for images currently under the cursor. */
280}
281
283 if (!m_image_set.empty())
284 {
285 for (auto& img : m_image_set)
286 {
287 DEBUG_ASSERT(img < helper->GetPanoramaPtr()->getNrOfImages());
288 // reset this button to its default system colour.
290 // remove the notification
292 }
293 }
294 m_image_set.clear();
296 m_stopUpdating = true;
297 ForceRedraw();
298}
299
304
306{
307
308 bool stop = false;
309 bool start = false;
310
311 if (m_constantOn) {
312 if (e.Dragging() && !(m_holdLeft)) {
313 stop = true;
314 m_stopUpdating = true;
315 }
316
317 if (m_stopUpdating && !e.LeftIsDown() && !e.MiddleIsDown() && !e.RightIsDown()) {
318 start = true;
319 }
320 }
321
322 if (m_holdControl && !e.m_controlDown) {
323 m_holdControl = false;
324 if (!m_constantOn) {
325 stop = true;
326 }
327 }
328
329 if (!m_holdControl && e.m_controlDown) {
330 m_holdControl = true;
331 stop = false;
332 if (m_stopUpdating) {
333 start = true;
334 }
335 }
336
337 if (stop) {
338 this->StopUpdating();
339 } else if(start) {
340 this->ContinueUpdating();
341 }
342
343
344}
345
347
348 if (keycode == WXK_CONTROL) {
349 if (pressed) {
350 m_holdControl = true;
352 } else {
353 if(m_holdControl) {
354 StopUpdating();
355 }
356 m_holdControl = false;
357 }
358 }
359}
360
363 if (constant_on_in) {
364 m_stopUpdating = false;
365 helper->SetStatusMessage(_("Move the mouse over the images or image buttons to identify them."));
367 } else {
368 m_stopUpdating = true;
369 StopUpdating();
370 }
371
372}
373
374
376{
377 if (m_stopUpdating) {
378 return;
379 }
380
381 std::set<unsigned int> new_image_set = helper->GetImageNumbersUnderMouse();
382
383 //UpdateIdentifyTools() will unrequest notification for the old indicators,
384 //reset the button colors, request notification for the new ones, swap in
385 //m_image_set with new_image_set, and force a redraw for all three
386 //PreviewIdentifyTool objects in GLPreviewFrame. This has the effect of
387 //displaying the indicators in both the preview and overview when you move
388 //your mouse over either when the Identify button is toggled on.
390
391 // if there is exactly two images, tell the user they can click to edit CPs.
392 if (m_image_set.size() == 2)
393 {
394 helper->SetStatusMessage(_("Click to create or edit control points here."));
395 } else {
396 helper->SetStatusMessage(_("Move the mouse over the images or image buttons to identify them."));
397 }
398}
399
401{
402 // we draw the partly transparent identification boxes over the top of the
403 // entire stack of images in m_image_set so that the extents of images in the
404 // background are clearly marked.
405 const unsigned int num_images = m_image_set.size();
406 // draw the actual images
407 // the preview draws them in reverse order, so the lowest numbered appears
408 // on top. We will folow this convention to avoid confusion.
410 for (auto it = m_image_set.rbegin(); it != m_image_set.rend(); ++it)
411 {
412 DEBUG_ASSERT(*it < helper->GetPanoramaPtr()->getNrOfImages());
414 DrawImage(*it,
416 }
418
419 // now draw the identification boxes
422 unsigned int image_counter = 0;
423 const unsigned int canvasWidth = helper->GetViewStatePtr()->GetOptions()->getWidth();
424 const unsigned int canvasHeight = helper->GetViewStatePtr()->GetOptions()->getHeight();
425 for (auto it = m_image_set.rbegin(); it != m_image_set.rend(); ++it)
426 {
428 // Use the mask to alter the shape of the identification boxes, but
429 // replace the normal image texture with the identification box itself.
431 {
434 }
435 // we want to shift the texture so it lines up with the cropped region.
436 glPushMatrix();
438 GetSrcImage(*it);
439 int width = src->getSize().width(), height = src->getSize().height();
440 vigra::Rect2D crop_region = src->getCropRect();
441 // pick a texture depending on crop mode and move it to the cropped area
442 switch (src->getCropMode())
443 {
446 // change the crop region to a square around the circle.
447 if (crop_region.width() < crop_region.height())
448 {
449 // too tall, move top and bottom.
450 int diff = (crop_region.width() - crop_region.height()) / 2;
451 // diff is negative, so we will shrink the border in the y
452 // direction.
453 crop_region.addBorder(0, diff);
454 } else if (crop_region.width() > crop_region.height())
455 {
456 // too wide, move left and right
457 int diff = (crop_region.height() - crop_region.width()) / 2;
458 crop_region.addBorder(diff, 0);
459 }
460 {
461 float diameter = (float) crop_region.width();
462 glScalef((float) width / diameter,
463 (float) height / diameter, 1.0);
464 glTranslatef(-(float) crop_region.left() / (float) width,
465 -(float) crop_region.top() / (float) height,
466 0.0);
467 }
468 break;
470 // get the biggest rectangle contained by both the image
471 // and the cropped area.
472 crop_region &= vigra::Rect2D(src->getSize());
474 glScalef((float) width / (float) crop_region.width(),
475 (float) height / (float) crop_region.height(),
476 1.0);
477 glTranslatef(-(float) crop_region.left() / (float) width,
478 -(float) crop_region.top() / (float) height,
479 0.0);
480 break;
483 break;
484 }
485 // draw the image in this texture
487 unsigned char r,g,b;
490 glColor3ub(r,g,b);
493 glPopMatrix();
494 // tell the preview frame to update the button to show the same colour.
496 // draw number
497 // this code works only for the preview window, not for the panosphere or mosaic plane
498 if (m_showNumbers)
499 {
503 transform.createInvTransform(*img, *opts);
507 {
510 {
514 };
516 glPushMatrix();
518 number << *it;
519 int textWidth = 0;
520 unsigned char* listIndex = new unsigned char[number.Length()];
521 for (size_t i = 0; i < number.Length(); ++i)
522 {
523 textWidth += m_glyphWidth[number[i].GetValue() - 48];
524 listIndex[i] = number[i].GetValue() - 48;
525 }
526 float scaleFactor = std::min(canvasWidth, canvasHeight) / static_cast<float>(FONT_TEXTURE_HEIGHT) / 10;
527 imageCenterPano.x = std::min<double>(std::max<double>(imageCenterPano.x, textWidth*scaleFactor / 2.0), canvasWidth - textWidth * scaleFactor / 2.0);
528 imageCenterPano.y = std::min<double>(std::max<double>(imageCenterPano.y, FONT_TEXTURE_HEIGHT*scaleFactor / 2.0), canvasHeight - FONT_TEXTURE_HEIGHT * scaleFactor / 2.0);
530 glScalef(scaleFactor, scaleFactor, 1.0);
535 glPopMatrix();
536 delete[] listIndex;
537 };
538 };
539 }
540 // set stuff back how we found it.
543 glColor3ub(255, 255, 255);
544}
545
547{
548 // Delay drawing of images, so we can show them on top of the others.
549 DEBUG_ASSERT(image < helper->GetPanoramaPtr()->getNrOfImages());
550 if (m_image_set.count(image)) return false;
551 return true;
552}
553
555{
556 DEBUG_ASSERT(image < helper->GetPanoramaPtr()->getNrOfImages());
557 // Add this image to the set of images drawn.
558 if (!m_image_set.count(image))
559 {
560 // it is not already in the set. Add it now
561 m_image_set.insert(image);
562 // we want notification of when it is drawn so we can delay drawing.
563 helper->NotifyMeBeforeDrawing(image, this);
564 // now we want a redraw.
565 ForceRedraw();
566 }
567 m_mouse_over_image = image;
569}
570
572{
574 {
575 // set the colour to the image the user just moused off to the default.
579 // now redraw without the indicator.
580 ForceRedraw();
582 }
583}
584
585// generate a colour given how many colours we need and an index.
587 unsigned int count,
588 unsigned char &red,
589 unsigned char &green,
590 unsigned char &blue)
591{
593 // the first one is red, the rest are evenly spaced throughout the spectrum
594 float hue = ((float) index / (float) count) * 6.0;
595 if (hue < 1.0)
596 {
597 // red to yellow
598 red = 255;
599 green = (unsigned char) (hue * 255.0);
600 blue = 0;
601 } else if (hue < 2.0) {
602 // yellow to green
603 red = (unsigned char) ((-hue + 2.0) * 255.0);
604 green = 255;
605 blue = 0;
606 } else if (hue < 3.0) {
607 // green to cyan
608 red = 0;
609 green = 255;
610 blue = (unsigned char) ((hue - 2.0) * 255.0);
611 } else if (hue < 4.0) {
612 // cyan to blue
613 red = 0;
614 green = (unsigned char) ((-hue + 4.0) * 255.0);
615 blue = 255;
616 } else if (hue < 5.0) {
617 // blue to magenta
618 red = (unsigned char) ((hue - 4.0) * 255.0);
619 green = 0;
620 blue = 255;
621 } else {
622 // magenta to red
623 red = 255;
624 green = 0;
625 blue = (unsigned char) ((-hue + 6.0) * 255.0);
626 }
627}
628
630{
631
632
633 if ( e.LeftDown() && helper->IsMouseOverPano())
634 {
635 m_holdLeft = true;
636 }
637
638 if (m_holdLeft && e.LeftUp() && (!m_image_set.empty()))
639 {
640 m_holdLeft = false;
641 if (m_constantOn || e.CmdDown())
642 {
643 // when there are at least two images with indicators shown, show the
644 // control point editor with the first two images when left clicked.
645 if(m_image_set.size()>1)
646 {
648 *(++m_image_set.begin()));
649 }
650 else
651 {
653 };
654 MainFrame::Get()->Raise();
655 }
656 }
657
658 if (m_holdLeft && !(e.LeftIsDown())) {
659 m_holdLeft = false;
660 }
661
662 if (m_constantOn) {
663 if (e.ButtonUp() && !e.MiddleIsDown() && !e.RightIsDown()) {
664 m_stopUpdating = false;
666 }
667 }
668
669}
670
672{
673 // If we are currently showing indicators for some of the images, we want
674 // to work out which ones are not in the new set, so we can set their
675 // buttons back to the system colour.
676 if (!m_image_set.empty())
677 {
679 std::set_difference (m_image_set.begin(), m_image_set.end(),
680 new_image_set.begin(), new_image_set.end(),
681 std::inserter(difference,difference.end()));
682 if (!difference.empty())
683 {
684 for (auto& img : difference)
685 {
686 DEBUG_ASSERT(img < helper->GetPanoramaPtr()->getNrOfImages());
687 // reset this button to its default system colour.
689 // remove the notification
691 }
692 }
693 }
694
695 // now request to be notified when drawing the new ones.
696 if (!new_image_set.empty())
697 {
699 std::set_difference(new_image_set.begin(), new_image_set.end(),
700 m_image_set.begin(), m_image_set.end(),
701 std::inserter(difference, difference.end()));
702 if (!difference.empty())
703 {
704 for (auto& img : difference)
705 {
706 DEBUG_ASSERT(img < helper->GetPanoramaPtr()->getNrOfImages());
707 // get notification of when this is about to be drawn.
708 helper->NotifyMeBeforeDrawing(img, this);
709 }
710 }
711 };
712 // remember the new set.
714
715 // Redraw with new indicators. Since the indicators aren't part of the
716 // panorama and none of it is likely to change, we have to persuade the
717 // viewstate that a redraw is required.
718 ForceRedraw();
719}
720
#define FONT_TEXTURE_HEIGHT
#define circle_ts
#define rect_ts
#define circle_middle
wxBitmap GenerateFontTexture(const int textureHeight, int &textureWidth, std::vector< int > &glyphWidth)
#define circle_border_inner
#define circle_border_outer
#define circle_ts_multiple
#define circle_border_peak
The OpenGL preview frame.
void UpdateIdentifyTools(std::set< unsigned int > new_image_set)
void SetImageButtonColour(unsigned int image_nr, unsigned char red, unsigned char green, unsigned char blue)
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 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.
unsigned int getWidth() const
unsigned int getHeight() const
get panorama height
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
void ShowMaskEditor(size_t imgNr, bool switchToCropMode=false)
opens the mask/crop editor with the given image selected
void MouseMoveEvent(double x, double y, wxMouseEvent &e)
Notify when the mouse pointer has moved over the panorama preview.
void AfterDrawImagesEvent()
Draw (using OpenGL) images above the others.
void setConstantOn(bool constant_on_in)
PreviewIdentifyTool(ToolHelper *helper, GLPreviewFrame *owner, bool showNumbers)
constructor
bool BeforeDrawImageEvent(unsigned int image)
Draw what the tool requires just before a given image is drawn.
std::set< unsigned int > m_image_set
Set of image numbers of the images we are displaying highlighted.
void UpdateWithNewImageSet(std::set< unsigned int > new_image_set)
static unsigned int font_list
OpenGL call list id for font.
GLPreviewFrame * m_preview_frame
void Activate()
Switch on a tool.
static unsigned int font_tex
OpenGL texture name for the font texture.
static unsigned int circle_border_tex
OpenGL texture name for the circular border texture.
unsigned int m_mouse_over_image
The image the user last placed their mouse over the button for.
static std::vector< int > m_glyphWidth
glyph width for font
void ImagesUnderMouseChangedEvent()
Notify when the images directly underneath the mouse pointer have changed.
void MouseButtonEvent(wxMouseEvent &e)
Show control point editor if mouse is over two images.
void KeypressEvent(int keycode, int modifiers, int pressed)
void HighlightColour(unsigned int index, unsigned int count, unsigned char &red, unsigned char &green, unsigned char &blue)
Generate a colour given how many colours we need and an index.
void StopShowingImages()
Notification for when moving the mouse off an image button.
static unsigned int rectangle_border_tex
OpenGL texture name for the rectangular border texture.
void ShowImageNumber(unsigned int image)
Notification for when moving the mouse on an image button.
void BindTexture(unsigned int image_number)
bool IsMouseOverPano()
Definition ToolHelper.h:136
void NotifyMe(Event event, Tool *tool)
ViewState * GetViewStatePtr()
VisualizationState * GetVisualizationStatePtr()
void NotifyMeBeforeDrawing(unsigned int image_nr, Tool *tool)
std::set< unsigned int > GetImageNumbersUnderMouse()
void DoNotNotifyMeBeforeDrawing(unsigned int image_nr, Tool *tool)
@ IMAGES_UNDER_MOUSE_CHANGE
Definition ToolHelper.h:83
@ 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
bool GetSupportMultiTexture() const
Definition ViewState.h:106
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
#define DEBUG_ASSERT(cond)
Definition utils.h:80
std::set< unsigned int > UIntSet
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