1 // -*- c-basic-offset: 4 -*-
2
3 /** @file LensCalImageCtrl.cpp
4 *
5 * @brief implementation of preview for lens calibration gui
6 *
7 * @author T. Modes
8 *
9 */
10
11 /* This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This software is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public
22 * License along with this software. If not, see
23 * <http://www.gnu.org/licenses/>.
24 *
25 */
26
27 #include "panoinc_WX.h"
28 #include "panoinc.h"
29 #include "base_wx/platform.h"
30 #include "LensCalImageCtrl.h"
31 #include "vigra/transformimage.hxx"
32 #include "nona/RemappedPanoImage.h"
33 #include "nona/ImageRemapper.h"
34 #include "LensCalApp.h"
35 #include "base_wx/wxcms.h"
36
BEGIN_EVENT_TABLE(LensCalImageCtrl,wxPanel)37 BEGIN_EVENT_TABLE(LensCalImageCtrl, wxPanel)
38 EVT_SIZE(LensCalImageCtrl::Resize)
39 EVT_PAINT(LensCalImageCtrl::OnPaint)
40 EVT_MOUSE_EVENTS(LensCalImageCtrl::OnMouseEvent)
41 END_EVENT_TABLE()
42
43 // init some values
44 LensCalImageCtrl::LensCalImageCtrl() : wxPanel()
45 {
46 m_showLines=true;
47 m_imageLines=NULL;
48 m_edge.Create(0,0,true);
49 m_previewMode=mode_original;
50 m_projection=HuginBase::SrcPanoImage::RECTILINEAR;
51 m_focallength=30;
52 m_cropfactor=1;
53 m_a=0;
54 m_b=0;
55 m_c=0;
56 m_d=0;
57 m_e=0;
58 m_monitorProfile = NULL;
59 // load monitor profile
60 wxString profileName;
61 HuginBase::Color::GetMonitorProfile(profileName, m_monitorProfile);
62 m_hasMonitorProfile = !profileName.IsEmpty();
63 };
64
GetMode()65 const LensCalImageCtrl::LensCalPreviewMode LensCalImageCtrl::GetMode()
66 {
67 return m_previewMode;
68 };
69
OnMouseEvent(wxMouseEvent & e)70 void LensCalImageCtrl::OnMouseEvent(wxMouseEvent &e)
71 {
72 if(e.Entering() || e.Leaving())
73 {
74 e.Skip();
75 return;
76 };
77 if(!e.LeftIsDown() && !e.RightIsDown())
78 {
79 e.Skip();
80 return;
81 };
82 if(m_imageLines==NULL)
83 {
84 return;
85 };
86 vigra::Point2D pos(e.GetPosition().x,e.GetPosition().y);
87 HuginLines::Lines lines=m_imageLines->GetLines();
88 if(lines.empty())
89 return;
90 int found_line=-1;
91 if(m_previewMode==mode_corrected)
92 {
93 HuginBase::PTools::Transform trans;
94 trans.createTransform(m_panoimage,m_opts);
95 double x;
96 double y;
97 if(!trans.transformImgCoord(x,y,pos.x,pos.y))
98 return;
99 pos.x=x;
100 pos.y=y;
101 }
102 else
103 {
104 pos.x=hugin_utils::roundi(pos.x/m_scale);
105 pos.y=hugin_utils::roundi(pos.y/m_scale);
106 };
107
108 //find line which is nearest the clicked position
109 std::vector<double> min_distance(lines.size(),10000);
110 double shortest_distance=10000;
111 for(unsigned int i=0;i<lines.size();i++)
112 {
113 if(lines[i].status==HuginLines::valid_line || lines[i].status==HuginLines::valid_line_disabled)
114 {
115 for(unsigned int j=0;j<lines[i].line.size();j++)
116 {
117 double distance=(lines[i].line[j]-pos).magnitude();
118 if(distance<min_distance[i])
119 min_distance[i]=distance;
120 };
121 };
122 if(min_distance[i]<shortest_distance)
123 {
124 shortest_distance=min_distance[i];
125 if(shortest_distance<50)
126 {
127 found_line=i;
128 };
129 };
130 };
131 if(found_line==-1)
132 {
133 return;
134 };
135 if(e.LeftIsDown())
136 {
137 lines[found_line].status=HuginLines::valid_line_disabled;
138 }
139 else
140 {
141 lines[found_line].status=HuginLines::valid_line;
142 };
143 m_imageLines->SetLines(lines);
144 wxGetApp().GetLensCalFrame()->UpdateListString(m_imageIndex);
145 DrawView();
146 Refresh();
147 };
148
DrawView()149 void LensCalImageCtrl::DrawView()
150 {
151 if(m_imageLines==NULL)
152 return;
153 if(m_previewMode==mode_corrected)
154 {
155 m_display_img.Create(m_remapped_img.GetWidth(), m_remapped_img.GetHeight());
156 }
157 else
158 {
159 m_display_img.Create(m_scaled_img.GetWidth(), m_scaled_img.GetHeight());
160 };
161 wxMemoryDC memDC(m_display_img);
162 HuginBase::PTools::Transform trans;
163 // copy resized image into buffer
164 if(m_previewMode==mode_corrected)
165 {
166 trans.createInvTransform(m_panoimage,m_opts);
167 memDC.DrawBitmap(m_remapped_img,0,0,false);
168 }
169 else
170 {
171 memDC.DrawBitmap(m_scaled_img,0,0,false);
172 };
173 if(m_showLines)
174 {
175 HuginLines::Lines lines=m_imageLines->GetLines();
176 for(unsigned int i=0;i<lines.size();i++)
177 {
178 if(lines[i].line.size()<2)
179 continue;
180 switch(lines[i].status)
181 {
182 case HuginLines::valid_line:
183 memDC.SetPen(wxPen(wxColour(0,255,0), 1, wxPENSTYLE_SOLID));
184 break;
185 case HuginLines::valid_line_disabled:
186 memDC.SetPen(wxPen(wxColour(255, 0, 0), 1, wxPENSTYLE_SOLID));
187 break;
188 default:
189 memDC.SetPen(wxPen(wxColour(128, 128, 128), 1, wxPENSTYLE_SOLID));
190 break;
191 };
192 for(unsigned int j=0;j<lines[i].line.size()-1;j++)
193 {
194 int x1,y1,x2,y2;
195 if(m_previewMode==mode_corrected)
196 {
197 double x,y;
198 if(!trans.transformImgCoord(x,y,lines[i].line[j].x,lines[i].line[j].y))
199 continue;
200 x1=hugin_utils::roundi(x);
201 y1=hugin_utils::roundi(y);
202 if(!trans.transformImgCoord(x,y,lines[i].line[j+1].x,lines[i].line[j+1].y))
203 continue;
204 x2=hugin_utils::roundi(x);
205 y2=hugin_utils::roundi(y);
206 }
207 else
208 {
209 x1=hugin_utils::roundi(m_scale*lines[i].line[j].x);
210 y1=hugin_utils::roundi(m_scale*lines[i].line[j].y);
211 x2=hugin_utils::roundi(m_scale*lines[i].line[j+1].x);
212 y2=hugin_utils::roundi(m_scale*lines[i].line[j+1].y);
213 };
214 memDC.DrawLine(x1,y1,x2,y2);
215 };
216 };
217 };
218 memDC.SelectObject(wxNullBitmap);
219 };
220
Resize(wxSizeEvent & e)221 void LensCalImageCtrl::Resize( wxSizeEvent & e )
222 {
223 if(m_imageLines==NULL || !m_img.Ok() || (m_previewMode==mode_edge && !m_edge.Ok()))
224 {
225 m_scaled_img.Create(0,0);
226 m_display_img.Create(0,0);
227 Refresh(true);
228 return;
229 }
230 int x = GetSize().x;
231 int y = GetSize().y;
232 // scale to fit the window
233 int new_width;
234 int new_height;
235
236 float r_img = (float)m_img.GetWidth() / (float)m_img.GetHeight();
237 float r_window = (float)x/(float)y;
238 if ( r_img > r_window )
239 {
240 m_scale = (float)x / m_img.GetWidth();
241 new_width = x;
242 new_height = hugin_utils::roundi(m_scale * m_img.GetHeight());
243 }
244 else
245 {
246 m_scale = (float)y / m_img.GetHeight();
247 new_height = y;
248 new_width = hugin_utils::roundi(m_scale * m_img.GetWidth());
249 }
250 switch(m_previewMode)
251 {
252 case mode_original:
253 m_scaled_img = wxBitmap(m_img.Scale (new_width, new_height));
254 break;
255 case mode_edge:
256 m_scaled_img = wxBitmap(m_edge.Scale(new_width,new_height,wxIMAGE_QUALITY_HIGH));
257 break;
258 case mode_corrected:
259 GenerateRemappedImage(new_width,new_height);
260 break;
261 };
262 // draw new view into offscreen buffer
263 DrawView();
264 // eventually update the view
265 Refresh(false);
266 };
267
268 // Define the repainting behaviour
OnPaint(wxPaintEvent & dc)269 void LensCalImageCtrl::OnPaint(wxPaintEvent & dc)
270 {
271 wxPaintDC paintDC(this);
272 if ( m_display_img.Ok() )
273 {
274 paintDC.DrawBitmap(m_display_img, 0,0, FALSE);
275 }
276 }
277
SetImage(ImageLineList * newList,unsigned int newIndex)278 void LensCalImageCtrl::SetImage(ImageLineList* newList, unsigned int newIndex)
279 {
280 m_imageLines=newList;
281 m_imageIndex=newIndex;
282 std::string filename(newList->GetFilename().mb_str(HUGIN_CONV_FILENAME));
283 ImageCache::EntryPtr img = ImageCache::getInstance().getImage(filename);
284 // we need to create a copy, otherwise the color management function will modifiy the image
285 // directly in the ImageCache
286 m_img = imageCacheEntry2wxImage(img).Copy();
287 if (!img->iccProfile->empty() || m_hasMonitorProfile)
288 {
289 HuginBase::Color::CorrectImage(m_img, *(img->iccProfile), m_monitorProfile);
290 }
291 SetEdgeImage();
292
293 wxSizeEvent e;
294 Resize (e);
295 }
296
SetEmptyImage()297 void LensCalImageCtrl::SetEmptyImage()
298 {
299 m_imageLines=NULL;
300 m_img.Create(0,0,true);
301 m_edge.Create(0,0,true);
302 m_remapped_img.Create(0,0,true);
303
304 wxSizeEvent e;
305 Resize(e);
306 };
307
SetShowLines(bool showLines)308 void LensCalImageCtrl::SetShowLines(bool showLines)
309 {
310 m_showLines=showLines;
311 // draw new view into offscreen buffer
312 DrawView();
313 // eventually update the view
314 Refresh(false);
315 };
316
SetMode(const LensCalPreviewMode newMode)317 void LensCalImageCtrl::SetMode(const LensCalPreviewMode newMode)
318 {
319 m_previewMode=newMode;
320 wxSizeEvent e;
321 Resize (e);
322 Refresh(true);
323 };
324
SetLens(const HuginBase::SrcPanoImage::Projection newProjection,const double newFocallength,const double newCropfactor)325 void LensCalImageCtrl::SetLens(const HuginBase::SrcPanoImage::Projection newProjection,const double newFocallength, const double newCropfactor)
326 {
327 m_projection=newProjection;
328 m_focallength=newFocallength;
329 m_cropfactor=newCropfactor;
330 };
331
SetLensDistortions(const double newA,const double newB,const double newC,const double newD,const double newE)332 void LensCalImageCtrl::SetLensDistortions(const double newA, const double newB, const double newC, const double newD, const double newE)
333 {
334 m_a=newA;
335 m_b=newB;
336 m_c=newC;
337 m_d=newD;
338 m_e=newE;
339 if(m_previewMode==mode_corrected)
340 {
341 wxSizeEvent e;
342 Resize(e);
343 };
344 };
345
gray2RGB(vigra::UInt8 const & v)346 vigra::RGBValue<vigra::UInt8> gray2RGB(vigra::UInt8 const& v)
347 {
348 return vigra::RGBValue<vigra::UInt8>(v,v,v);
349 }
350
SetEdgeImage()351 void LensCalImageCtrl::SetEdgeImage()
352 {
353 vigra::BImage* edgeImage=m_imageLines->GetEdgeImage();
354 if(edgeImage->width()>0 && edgeImage->height()>0)
355 {
356 // we need to convert to RGB
357 m_edgeImage.resize(edgeImage->width(),edgeImage->height());
358 vigra::transformImage(srcImageRange(*edgeImage), destImage(m_edgeImage), &gray2RGB);
359 m_edge.SetData((unsigned char*)m_edgeImage.data(),m_edgeImage.width(),m_edgeImage.height(),true);
360 }
361 else
362 {
363 m_edgeImage.resize(0,0);
364 m_edge.Create(0,0,true);
365 };
366 wxSizeEvent e;
367 Resize(e);
368 };
369
GenerateRemappedImage(const unsigned int newWidth,const unsigned int newHeight)370 void LensCalImageCtrl::GenerateRemappedImage(const unsigned int newWidth,const unsigned int newHeight)
371 {
372 HuginBase::Nona::RemappedPanoImage<vigra::BRGBImage,vigra::BImage>* remapped=new HuginBase::Nona::RemappedPanoImage<vigra::BRGBImage,vigra::BImage>;
373 //generate SrcPanoImage with current settings
374 m_panoimage=*(m_imageLines->GetPanoImage());
375 m_panoimage.setProjection(m_projection);
376 m_panoimage.setExifFocalLength(m_focallength);
377 m_panoimage.setCropFactor(m_cropfactor);
378 m_panoimage.setExposureValue(0);
379 m_panoimage.setVar("a",m_a);
380 m_panoimage.setVar("b",m_b);
381 m_panoimage.setVar("c",m_c);
382 m_panoimage.setVar("d",m_d);
383 m_panoimage.setVar("e",m_e);
384 double hfov=HuginBase::SrcPanoImage::calcHFOV(m_panoimage.getProjection(),m_panoimage.getExifFocalLength(),m_panoimage.getCropFactor(),m_panoimage.getSize());
385 m_panoimage.setHFOV(hfov);
386
387 std::string filename(m_imageLines->GetFilename().mb_str(HUGIN_CONV_FILENAME));
388 ImageCache::EntryPtr img = ImageCache::getInstance().getImage(filename);
389 //fill options with current settings
390 switch(m_projection)
391 {
392 case HuginBase::SrcPanoImage::RECTILINEAR:
393 m_opts.setProjection(HuginBase::PanoramaOptions::RECTILINEAR);
394 break;
395 case HuginBase::SrcPanoImage::PANORAMIC:
396 m_opts.setProjection(HuginBase::PanoramaOptions::CYLINDRICAL);
397 break;
398 case HuginBase::SrcPanoImage::CIRCULAR_FISHEYE:
399 case HuginBase::SrcPanoImage::FULL_FRAME_FISHEYE:
400 m_opts.setProjection(HuginBase::PanoramaOptions::FULL_FRAME_FISHEYE);
401 break;
402 case HuginBase::SrcPanoImage::EQUIRECTANGULAR:
403 m_opts.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR);
404 break;
405 case HuginBase::SrcPanoImage::FISHEYE_ORTHOGRAPHIC:
406 m_opts.setProjection(HuginBase::PanoramaOptions::ORTHOGRAPHIC);
407 break;
408 case HuginBase::SrcPanoImage::FISHEYE_STEREOGRAPHIC:
409 m_opts.setProjection(HuginBase::PanoramaOptions::STEREOGRAPHIC);
410 break;
411 case HuginBase::SrcPanoImage::FISHEYE_EQUISOLID:
412 m_opts.setProjection(HuginBase::PanoramaOptions::EQUISOLID);
413 break;
414 case HuginBase::SrcPanoImage::FISHEYE_THOBY:
415 m_opts.setProjection(HuginBase::PanoramaOptions::THOBY_PROJECTION);
416 break;
417 };
418 m_opts.setHeight(newHeight);
419 m_opts.setWidth(newWidth,false);
420 m_opts.setHFOV(m_panoimage.getHFOV(),false);
421 m_opts.setROI(vigra::Rect2D(0,0,m_opts.getWidth(),m_opts.getHeight()));
422 //now remap image
423 remapped->setPanoImage(m_panoimage,m_opts, m_opts.getROI());
424 remapped->remapImage(vigra::srcImageRange(*(img->get8BitImage())),vigra_ext::INTERP_CUBIC, wxGetApp().GetLensCalFrame());
425 m_remappedImage=remapped->m_image;
426 m_remapped_img.SetData((unsigned char*)m_remappedImage.data(),m_remappedImage.width(),m_remappedImage.height(),true);
427 // apply color profiles
428 if (!img->iccProfile->empty() || m_hasMonitorProfile)
429 {
430 HuginBase::Color::CorrectImage(m_remapped_img, *(img->iccProfile), m_monitorProfile);
431 }
432 delete remapped;
433 };
434
IMPLEMENT_DYNAMIC_CLASS(LensCalImageCtrl,wxPanel)435 IMPLEMENT_DYNAMIC_CLASS(LensCalImageCtrl, wxPanel)
436
437 LensCalImageCtrlXmlHandler::LensCalImageCtrlXmlHandler() : wxXmlResourceHandler()
438 {
439 AddWindowStyles();
440 }
441
DoCreateResource()442 wxObject *LensCalImageCtrlXmlHandler::DoCreateResource()
443 {
444 XRC_MAKE_INSTANCE(cp, LensCalImageCtrl)
445 cp->Create(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle(wxT("style")), GetName());
446 SetupWindow(cp);
447 return cp;
448 }
449
CanHandle(wxXmlNode * node)450 bool LensCalImageCtrlXmlHandler::CanHandle(wxXmlNode *node)
451 {
452 return IsOfClass(node, wxT("LensCalCanvas"));
453 }
454
455 IMPLEMENT_DYNAMIC_CLASS(LensCalImageCtrlXmlHandler, wxXmlResourceHandler)
456