24 #include "wx/msw/wrapwin.h"
29 #include "hugin_config.h"
41 #include "vigra/stdimage.hxx"
42 #include "vigra/resizeimage.hxx"
51 #if defined HAVE_EPOXY && HAVE_EPOXY
57 #include <wx/display.h>
60 #include <OpenGL/gl.h>
61 #include <OpenGL/glu.h>
68 #include "wx/mstream.h"
73 #include <exiv2/exiv2.hpp>
89 unsigned int display_list)
92 TexturesMap::iterator it;
98 glColor4f(1.0,1.0,1.0,1.0);
99 if (it->second->GetUseAlpha() || it->second->GetHasActiveMasks())
102 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
112 float scale[4] = {
static_cast<float>(es / img->getWhiteBalanceRed()),
114 static_cast<float>(es /img->getWhiteBalanceBlue()),
117 glCallList(display_list);
125 if (scale[0] > 1.0 || scale[1] > 1.0 || scale[2] > 1.0)
129 glBlendFunc(GL_DST_COLOR, GL_ONE);
130 glColor4f(1.0, 1.0, 1.0, 1.0);
133 bool r = (scale[0] > 2.0);
134 bool g = (scale[1] > 2.0);
135 bool b = (scale[2] > 2.0);
136 unsigned short int count = 0;
137 while ((r || g || b) && count < 9)
139 glColor4f(r ? 1.0 : 0.0, g ? 1.0 : 0.0, b ? 1.0 : 0.0, 1.0);
140 glCallList(display_list);
141 if (r) scale[0] /= 2.0;
142 if (g) scale[1] /= 2.0;
143 if (b) scale[2] /= 2.0;
144 r = (scale[0] > 2.0);
145 g = (scale[1] > 2.0);
146 b = (scale[2] > 2.0);
150 if (scale[0] > 1.0 || scale[1] > 1.0 || scale[2] > 1.0)
153 scale[0] -= 1.0; scale[1] -= 1.0; scale[2] -= 1.0;
155 glCallList(display_list);
157 glEnable(GL_TEXTURE_2D);
159 glColor3f(1.0, 1.0, 1.0);
163 glCallList(display_list);
164 if (it->second->GetUseAlpha() || it->second->GetHasActiveMasks())
174 TexturesMap::iterator it;
179 return it->second->GetNumber();
185 TexturesMap::iterator it;
197 glActiveTexture(GL_TEXTURE1);
198 glDisable(GL_TEXTURE_2D);
199 glActiveTexture(GL_TEXTURE0);
201 glDisable(GL_TEXTURE_2D);
206 glDisable(GL_TEXTURE_2D);
248 double total_fov = 0.0;
249 for (
unsigned int image_index = 0; image_index < num_images; image_index++)
252 double aspect = double(src->getSize().height())
253 /
double(src->getSize().width());
254 total_fov += src->getHFOV() * aspect;
264 double ideal_texels_used = 0.0;
265 for (
unsigned int image_index = 0; image_index < num_images; image_index++)
269 TexturesMap::iterator it;
284 unsigned int max_tex_width_p = int(log2(img_p->getSize().width())),
285 max_tex_height_p = int(log2(img_p->getSize().height()));
289 if (biggest < max_tex_width_p) max_tex_width_p = biggest;
290 if (biggest < max_tex_height_p) max_tex_height_p = biggest;
292 std::cout <<
"Texture size for image " << image_index <<
" is "
293 << (1 << max_tex_width_p) <<
" by "
294 << (1 << max_tex_height_p) <<
"\n";
296 std::cout <<
"About to create new TextureInfo for "
297 << img_p->getFilename()
299 std::pair<std::map<TextureKey, TextureInfo>::iterator,
bool> ins;
300 ins =
textures.insert(std::pair<TextureKey, TextureInfo>
306 texinfo = &((ins.first)->second);
310 texinfo = &(it->second);
314 double hfov = img_p->getHFOV(),
315 aspect = double (texinfo->
height) / double (texinfo->
width),
318 ideal_tex_width = sqrt(ideal_texels / aspect),
319 ideal_tex_height = aspect * ideal_tex_width;
321 ideal_texels_used += ideal_texels;
322 std::cout <<
"Ideal mip size: " << ideal_tex_width <<
" by "
323 << ideal_tex_height <<
"\n";
327 int mip_level = max_mip_level - ceil((ideal_tex_width > ideal_tex_height)
328 ? log2(ideal_tex_width) : log2(ideal_tex_height));
338 if (mip_level > max_mip_level) mip_level = max_mip_level;
340 if (mip_level < 0) mip_level = 0;
342 int mip_width_p = texinfo->
width_p - mip_level,
343 mip_height_p = texinfo->
height_p - mip_level;
346 if (mip_width_p < 0) mip_width_p = 0;
347 if (mip_height_p < 0) mip_height_p = 0;
351 texels_used += 1 << (mip_width_p + mip_height_p);
352 std::cout <<
"biggest mipmap of image " << image_index <<
" is "
353 << (1 << mip_width_p) <<
" by " << (1 << mip_height_p)
354 <<
" (level " << mip_level <<
").\n";
355 std::cout <<
"Ideal texels used " << int(ideal_texels_used)
356 <<
", actually used " << texels_used <<
".\n\n";
357 if (texinfo->
min_lod != mip_level)
360 if (texinfo->
min_lod > mip_level)
364 (texinfo->
min_lod > max_mip_level) ?
365 max_mip_level : texinfo->
min_lod - 1,
377 double hfov = img_p->getHFOV(),
378 aspect = double (img_p->getSize().height())
379 /
double (img_p->getSize().width()),
382 ideal_tex_width = sqrt(ideal_texels / aspect),
383 ideal_tex_height = aspect * ideal_tex_width;
385 if (ideal_tex_width > img_p->getSize().width())
386 ideal_tex_width = img_p->getSize().width();
387 if (ideal_tex_height > img_p->getSize().height())
388 ideal_tex_height = img_p->getSize().height();
392 unsigned int tex_width_p = int(log2(ideal_tex_width)) + 1,
393 tex_height_p = int(log2(ideal_tex_height)) + 1;
397 if (biggest < tex_width_p) tex_width_p = biggest;
398 if (biggest < tex_height_p) tex_height_p = biggest;
402 ideal_texels_used += ideal_texels;
404 while ( (texels_used + (1 << (tex_width_p + tex_height_p)))
408 if ((
double) (1 << tex_height_p) / (
double) (1 << tex_width_p)
417 texels_used += 1 << (tex_width_p + tex_height_p);
419 || (it->second)->width_p != tex_width_p
420 || (it->second)->height_p != tex_height_p)
430 std::pair<TexturesMap::iterator, bool> ins;
435 std::make_shared<TextureInfo>(
view_state, tex_width_p, tex_height_p)
441 tex_width_p > tex_height_p ? tex_width_p : tex_height_p,
486 static int displaySize = 0;
487 if (displaySize == 0)
490 for (
size_t i = 0; i < wxDisplay::GetCount(); ++i)
492 const wxRect currentDisplaySize = wxDisplay(i).GetGeometry();
493 displaySize =
std::max(displaySize, currentDisplaySize.GetWidth() * currentDisplaySize.GetHeight());
499 return 4 * displaySize;
512 static unsigned int max_size_p = 0;
513 if (max_size_p)
return max_size_p;
516 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
523 max_size_p = int(log2(max_size));
524 DEBUG_INFO(
"Max texture size supported is " << max_size <<
525 " (2^" << max_size_p <<
")");
535 TexturesMap::iterator tex;
544 for (
unsigned int img = 0; img < num_images; img++)
547 if (ik == tex->first)
556 DEBUG_INFO(
"Removing old texture for " << tex->first.filename <<
".");
569 bool texture_photometric_correct,
573 TexturesMap::iterator it =
textures.find(
TextureKey(&state, &texture_photometric_correct));
578 std::shared_ptr<TextureInfo> tex(it->second);
579 tex->DefineLevels(min, max, texture_photometric_correct, dest_img, state);
594 unsigned int height_p_in)
596 m_viewState=new_view_state;
597 has_active_masks=
false;
599 width_p = width_p_in;
600 height_p = height_p_in;
601 width = 1 << width_p;
602 height = 1 << height_p;
609 glGenTextures(1, (GLuint*) &num);
611 glGenTextures(1, (GLuint*) &numMask);
620 glEnable(GL_TEXTURE_2D);
621 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
622 GL_LINEAR_MIPMAP_LINEAR);
624 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
625 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
629 static bool checked_anisotropic =
false;
630 static bool has_anisotropic;
631 static float anisotropy;
632 if (!checked_anisotropic)
635 #if defined HAVE_EPOXY && HAVE_EPOXY
636 if (epoxy_has_gl_extension(
"GL_EXT_texture_filter_anisotropic"))
638 if (GLEW_EXT_texture_filter_anisotropic)
641 has_anisotropic =
true;
642 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy);
643 DEBUG_INFO(
"Using anisotropic filtering at maximum value "
646 has_anisotropic =
false;
647 DEBUG_INFO(
"Anisotropic filtering is not available.");
649 checked_anisotropic =
true;
653 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
656 if(m_viewState->GetSupportMultiTexture())
659 glEnable(GL_TEXTURE_2D);
660 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
662 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
663 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
665 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,anisotropy);
667 GLenum error = glGetError();
668 if (error != GL_NO_ERROR)
670 DEBUG_ERROR(
"GL Error when setting texture parameters: "
671 << gluErrorString(error) <<
".");
679 glDeleteTextures(1, (GLuint*) &num);
680 glDeleteTextures(1, (GLuint*) &numMask);
687 if(m_viewState->GetSupportMultiTexture())
690 glEnable(GL_TEXTURE_2D);
692 glDisable(GL_TEXTURE_2D);
693 glActiveTexture(GL_TEXTURE0);
699 if(m_viewState->GetSupportMultiTexture())
701 glActiveTexture(GL_TEXTURE0);
702 glBindTexture(GL_TEXTURE_2D, num);
705 glBindTexture(GL_TEXTURE_2D, num);
709 if(m_viewState->GetSupportMultiTexture())
711 glActiveTexture(GL_TEXTURE1);
712 glEnable(GL_TEXTURE_2D);
713 glBindTexture(GL_TEXTURE_2D, numMask);
731 int max_mip_level = (width_p > height_p) ? width_p : height_p;
732 if (max > max_mip_level) max = max_mip_level;
742 ImageCache::getInstance().softFlush();
744 const std::string img_name = src_img.getFilename();
745 ImageCache::EntryPtr entry = ImageCache::getInstance().getImageIfAvailable(img_name);
749 m_imageRequest = ImageCache::getInstance().requestAsyncImage(img_name);
755 m_imageRequest->ready.push_back(
760 m_imageRequest->ready.push_back(
766 GLubyte* placeholder_image;
767 size_t placeholderWidth = 64;
768 size_t placeholderHeight = 64;
769 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,27,99)
770 Exiv2::Image::UniquePtr image;
772 Exiv2::Image::AutoPtr image;
774 bool hasPreview =
false;
777 image = Exiv2::ImageFactory::open(img_name.c_str());
782 std::cerr << __FILE__ <<
" " << __LINE__ <<
" Error opening file" << std::endl;
786 std::shared_ptr<Exiv2::PreviewManager> previews;
787 Exiv2::PreviewPropertiesList lists;
791 image->readMetadata();
792 previews = std::make_shared<Exiv2::PreviewManager>(*image);
793 lists = previews->getPreviewProperties();
795 catch (
const Exiv2::Error&)
807 int previewIndex = 0;
808 while (previewIndex < lists.size() - 1 && lists[previewIndex].width_ < 200 && lists[previewIndex].height_ < 200)
814 Exiv2::PreviewImage previewImage = previews->getPreviewImage(lists[previewIndex]);
815 wxMemoryInputStream stream(previewImage.pData(), previewImage.size());
817 wxLog::EnableLogging(
false);
818 if (rawImage.LoadFile(stream, wxString(previewImage.mimeType().c_str(), wxConvLocal), -1))
820 placeholderWidth = rawImage.GetWidth();
821 placeholderHeight = rawImage.GetHeight();
822 placeholder_image =
new GLubyte[placeholderWidth * placeholderHeight * 4];
824 for (
size_t y = 0; y < placeholderHeight; ++y)
826 for (
size_t x = 0; x < placeholderWidth; ++x)
828 placeholder_image[index++] = rawImage.GetRed(x, y);
829 placeholder_image[index++] = rawImage.GetGreen(x, y);
830 placeholder_image[index++] = rawImage.GetBlue(x, y);
831 placeholder_image[index++] = 63;
840 wxLog::EnableLogging(
true);
846 placeholder_image =
new GLubyte[placeholderWidth * placeholderHeight * 4];
848 for (
int i = 0; i < placeholderHeight; i++)
850 for (
int j = 0; j < placeholderWidth; j++)
853 GLubyte c = ((i / 8 + j / 8) % 2) ? 63 : 191;
854 placeholder_image[index++] = c;
855 placeholder_image[index++] = c;
856 placeholder_image[index++] = c;
858 placeholder_image[index++] = 63;
862 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, placeholderWidth, placeholderHeight,
863 GL_RGBA, GL_UNSIGNED_BYTE,
866 delete[] placeholder_image;
870 m_imageRequest = ImageCache::RequestPtr();
872 std::shared_ptr<vigra::BRGBImage> img = entry->get8BitImage();
873 std::shared_ptr<vigra::BImage> mask = entry->mask;
875 int wo = 1 << (width_p -
min), ho = 1 << (height_p - min);
876 if (wo < 1) wo = 1;
if (ho < 1) ho = 1;
879 vigra::BRGBImage out_img(wo, ho);
882 has_mask = mask->width() && mask->height();
883 vigra::UInt8Image *out_alpha;
884 if (has_mask) out_alpha =
new vigra::UInt8Image(wo, ho);
885 if (wo < 2 || ho < 2)
889 for (
int h = 0;
h < ho;
h++)
891 for (
int w = 0; w < wo; w++)
893 out_img[
h][w] = (*img)[0][0];
894 if (has_mask) (*out_alpha)[
h][w] = (*mask)[0][0];
916 cmsHPROFILE inputICC = NULL;
917 if (!entry->iccProfile->empty())
919 inputICC = cmsOpenProfileFromMem(entry->iccProfile->data(), entry->iccProfile->size());
921 cmsHTRANSFORM transform = NULL;
926 if (inputICC != NULL)
928 if (cmsGetColorSpace(inputICC) != cmsSigRgbData)
930 cmsCloseProfile(inputICC);
935 if (inputICC == NULL)
937 inputICC = cmsCreate_sRGBProfile();
940 transform = cmsCreateTransform(inputICC, TYPE_RGB_8,
942 INTENT_PERCEPTUAL, cmsFLAGS_BLACKPOINTCOMPENSATION);
945 if (photometric_correct)
947 DEBUG_INFO(
"Performing photometric correction");
960 invResponse(tempSrcImg);
964 std::vector<double> outLut;
983 double scale_x = (double)src_img.getSize().width() / (double)wo,
984 scale_y = (
double)src_img.getSize().height() / (double)ho;
985 #pragma omp parallel for
986 for (
int y = 0; y < ho; y++)
988 for (
int x = 0; x < wo; x++)
990 double sx = (double)x * scale_x,
991 sy = (
double)y * scale_y;
992 out_img[y][x] = invResponse(out_img[y][x],
996 if (transform != NULL)
998 cmsDoTransform(transform, out_img[y], out_img[y], out_img.width());
1005 if (transform != NULL)
1007 #pragma omp parallel for
1008 for (
int y = 0; y < ho; y++)
1010 cmsDoTransform(transform, out_img[y], out_img[y], out_img.width());
1014 if (transform != NULL)
1016 cmsDeleteTransform(transform);
1018 if (inputICC != NULL)
1020 cmsCloseProfile(inputICC);
1028 DEBUG_INFO(
"Defining mipmap levels " << min <<
" to " << max
1029 <<
" of texture " << num <<
", starting with a size of "
1030 << wo <<
" by " << ho <<
".");
1035 GLubyte *image =
new GLubyte[ho * wo * 4];
1036 GLubyte *pix_start = image;
1037 for (
int h = 0;
h < ho;
h++)
1039 for (
int w = 0; w < wo; w++)
1041 pix_start[0] = out_img[
h][w].red();
1042 pix_start[1] = out_img[
h][w].green();
1043 pix_start[2] = out_img[
h][w].blue();
1044 pix_start[3] = (*out_alpha)[
h][w];
1051 error = gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, wo, ho,
1052 GL_RGBA, GL_UNSIGNED_BYTE,
1058 error = gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, wo, ho,
1059 GL_RGB, GL_UNSIGNED_BYTE,
1060 (
unsigned char *) out_img.data());
1064 DEBUG_ERROR(
"GLU Error when building mipmap levels: "
1065 << gluErrorString(error) <<
".");
1067 error = glGetError();
1068 if (error != GL_NO_ERROR)
1070 DEBUG_ERROR(
"GL Error when building mipmap levels: "
1071 << gluErrorString(error) <<
".");
1083 if(has_active_masks)
1085 unsigned int maskSize=(width>height) ? width : height;
1089 for(
unsigned int i=0;i<masks.size();i++)
1090 masks[i].scale((
double)maskSize/srcImg.
getWidth(),(double)maskSize/srcImg.
getHeight());
1091 vigra::UInt8Image mask(maskSize,maskSize,255);
1098 GLubyte *image =
new GLubyte[maskSize * maskSize * 2];
1099 GLubyte *pix_start = image;
1100 for (
int h = 0;
h < maskSize;
h++)
1102 for (
int w = 0; w < maskSize; w++)
1105 pix_start[1] = mask[
h][w];
1109 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, maskSize, maskSize,
1110 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, image);
1113 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, maskSize,maskSize,GL_ALPHA, GL_UNSIGNED_BYTE,(
unsigned char *) mask.data());
1120 if(m_viewState->GetSupportMultiTexture())
1123 glDeleteTextures(1, (GLuint*) &numMask);
1125 glGenTextures(1, (GLuint*) &numMask);
1127 DefineMaskTexture(srcImg);
1138 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level);
1139 if(m_viewState->GetSupportMultiTexture())
1143 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level);
1146 GLenum error = glGetError();
1147 if (error != GL_NO_ERROR)
1149 DEBUG_ERROR(
"Error when setting the base mipmap level: "
1150 << gluErrorString(error) <<
".");
1155 bool *photometric_correct_ptr)
1164 return !(*
this < comp || comp < *
this);
1171 if (filename < comp.
filename)
return true;
1172 if (filename > comp.
filename)
return false;
1174 if (masks < comp.
masks)
return true;
1175 if (masks > comp.
masks)
return false;
1179 if (exposure < comp.
exposure)
return true;
1180 if (exposure > comp.
exposure)
return false;
1193 if (gamma < comp.
gamma)
return true;
1194 if (gamma > comp.
gamma)
return false;
1205 filename = source->getFilename();
1208 std::stringstream mask_ss;
1210 masks = mask_ss.str();
1213 white_balance_red = source->getWhiteBalanceRed();
1214 white_balance_blue = source->getWhiteBalanceBlue();
1215 EMoR_params = source->getEMoRParams();
1216 radial_vig_corr_coeff = source->getRadialVigCorrCoeff();
1217 vig_corr_mode = source->getVigCorrMode();
1218 response_type = source->getResponseType();
1219 gamma = source->getGamma();
1220 radial_distortion_red = source->getRadialDistortionRed();
1221 radial_distortion_blue = source->getRadialDistortionBlue();
double outputRangeCompression
void applyMask(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > img, HuginBase::MaskPolygonVector masks)
unsigned int GetTextureName(unsigned int image_number)
void DefineMaskTexture(const HuginBase::SrcPanoImage &srcImg)
std::vector< double > radial_distortion_blue
unsigned int GetMaxTotalTexels()
void SetMaxLevel(int level)
void SetOptions(const HuginBase::SrcPanoImage *source)
bool hasActiveMasks() const
returns true, if image has active masks
void DisableTexture(bool maskOnly=false)
void enforceMonotonicity(LUT &lut)
enforce monotonicity of an array (mostly used for lookup tables)
void printMaskLines(std::ostream &o, unsigned int newImgNr) const
writes all mask lines to stream, using given image number
#define DEBUG_ASSERT(cond)
include file for the hugin project
std::vector< double > radial_distortion_red
declaration of classes to work with mask
int getHeight() const
Get the height of the image in pixels.
void GetMonitorProfile(wxString &profileName, cmsHPROFILE &profile)
static huginApp * Get()
hack.. kind of a pseudo singleton...
HuginBase::SrcPanoImage::ResponseType response_type
HuginBase::PanoramaOptions * GetOptions()
void SetPhotometricCorrect(bool state)
empirical model of response
void DefineLevels(int min, int max, bool photometric_correct, const HuginBase::PanoramaOptions &dest_img, const HuginBase::SrcPanoImage &state)
std::size_t getNrOfImages() const
number of images.
float pow(float a, double b)
int getWidth() const
Get the width of the image in pixels.
void redrawPreview()
Display an updated version of the preview images.
void UpdateMask(const HuginBase::SrcPanoImage &srcImg)
static MainFrame * getMainFrame()
TextureManager * GetTextureManager()
bool HasMonitorProfile() const
return true if we found a suitable monitor profile and could loading it
std::vector< float > EMoR_params
std::vector< double > radial_vig_corr_coeff
TextureInfo(ViewState *new_view_state)
std::vector< MaskPolygon > MaskPolygonVector
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
HuginBase::Panorama * m_pano
void createEMoRLUT(const std::vector< float > ¶ms, VECTOR &lut)
const bool operator==(const TextureKey &comp) const
double getExposure() const
void BindTexture(unsigned int image_number)
void DrawImage(unsigned int image_number, unsigned int display_list)
const PanoramaOptions & getOptions() const
returns the options for this panorama
TextureManager(HuginBase::Panorama *pano, ViewState *view)
bool RequireRecalculateMasks(unsigned int image_nr)
bool RequireRecalculatePhotometric()
unsigned int GetMaxTextureSizePower()
virtual ~TextureManager()
vigra::triple< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImageRange(ROIImage< Image, Alpha > &img)
const bool operator<(const TextureKey &comp) const
HuginBase::SrcPanoImage * GetSrcImage(unsigned int image_nr)
All variables of a source image.
double white_balance_blue
bool GetSupportMultiTexture() const
void LoadingImageFinished(int min, int max, bool texture_photometric_correct, const HuginBase::PanoramaOptions &dest_img, const HuginBase::SrcPanoImage &state)
double outputExposureValue