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