27 #include <hugin_config.h>
31 #include <vigra/error.hxx>
32 #include <vigra/impex.hxx>
33 #include <vigra/codec.hxx>
47 template <
class SrcImgType,
class AlphaImgType,
class FlatImgType,
class DestImgType>
49 const AlphaImgType& srcAlpha,
50 const FlatImgType& srcFlat,
55 AlphaImgType& destAlpha,
59 template <
class PIXELTYPE>
63 static void usage(
const char* name)
65 std::cout << name <<
": correct lens distortion, vignetting and chromatic abberation" << std::endl
68 <<
"Usage: " << name <<
" [options] inputfile(s) " << std::endl
69 <<
" option are: " << std::endl
70 <<
" --green=db|a:b:c:d Correct radial distortion for all channels" << std::endl
71 <<
" Specify 'db' for database lookup or" << std::endl
72 <<
" the 4 coefficients a:b:c:d" << std::endl
73 <<
" --blue=db|a:b:c:d Correct radial distortion for blue channel," << std::endl
74 <<
" this is applied on top of the --green" << std::endl
75 <<
" distortion coefficients, use for TCA corr" << std::endl
76 <<
" Specify 'db' for database lookup or" << std::endl
77 <<
" the 4 coefficients a:b:c:d" << std::endl
78 <<
" --red=db|a:b:c:d Correct radial distortion for red channel," << std::endl
79 <<
" this is applied on top of the --green" << std::endl
80 <<
" distortion coefficients, use for TCA corr" << std::endl
81 <<
" Specify 'db' for database lookup or" << std::endl
82 <<
" the 4 coefficients a:b:c:d" << std::endl
83 <<
" --camera-maker=Maker Camera manufacturer, for database query" << std::endl
84 <<
" --camera-model=Cam Camera name, for database query" << std::endl
85 <<
" --lensname=Lens Lens name, for database query" << std::endl
86 <<
" Specify --camera-maker and --camera-model" << std::endl
87 <<
" for fixed lens cameras or --lensname" << std::endl
88 <<
" for interchangeable lenses." << std::endl
89 <<
" --focallength=50 Specify focal length in mm, for database query" << std::endl
90 <<
" --aperture=3.5 Specify aperture for vignetting data database query" << std::endl
91 <<
" --dont-rescale Do not rescale the image to avoid black borders." << std::endl
93 <<
" --flatfield=filename Vignetting correction by flatfield division" << std::endl
94 <<
" I = I / c, c = flatfield / mean(flatfield)" << std::endl
95 <<
" --vignetting=db|a:b:c:d Correct vignetting (by division)" << std::endl
96 <<
" Specify db for database look up or the " << std::endl
97 <<
" the 4 coefficients a:b:c:d" << std::endl
98 <<
" I = I / ( a + b*r^2 + c*r^4 + d*r^6)" << std::endl
99 <<
" --linear Do vignetting correction in linear color space" << std::endl
100 <<
" --gamma=value Gamma of input data. used for gamma correction" << std::endl
101 <<
" before and after flatfield correction" << std::endl
102 <<
" --help Display help (this text)" << std::endl
103 <<
" --output=name Set output filename. If more than one image is given," << std::endl
104 <<
" the name will be uses as suffix" << std::endl
105 <<
" (default suffix: _corr)" << std::endl
106 <<
" --compression=value Compression of the output files" << std::endl
107 <<
" For jpeg output: 0-100" << std::endl
108 <<
" For tiff output: PACKBITS, DEFLATE, LZW" << std::endl
109 <<
" --offset=X:Y Horizontal and vertical shift" << std::endl
110 <<
" --verbose Verbose" << std::endl;
114 int main(
int argc,
char* argv[])
117 const char* optstring =
"e:g:b:r:m:n:l:d:sf:c:i:t:ho:x:va:";
121 LINEAR_RESPONSE = 1000
123 static struct option longOptions[] =
125 {
"linear", no_argument, NULL, LINEAR_RESPONSE },
126 {
"green", required_argument, NULL,
'g' },
127 {
"blue", required_argument, NULL,
'b' },
128 {
"red", required_argument, NULL,
'r' },
129 {
"lensname", required_argument, NULL,
'l' },
130 {
"camera-maker", required_argument, NULL,
'm' },
131 {
"camera-model", required_argument, NULL,
'n' },
132 {
"aperture", required_argument, NULL,
'a' },
133 {
"focallength", required_argument, NULL,
'd' },
134 {
"flatfield", required_argument, NULL,
'f' },
135 {
"dont-rescale", no_argument, NULL,
's' },
136 {
"vignetting", required_argument, NULL,
'c' },
137 {
"gamma", required_argument, NULL,
'i' },
138 {
"threads", required_argument, NULL,
't' },
139 {
"output", required_argument, NULL,
'o' },
140 {
"compression", required_argument, NULL,
'e' },
141 {
"offset", required_argument, NULL,
'x' },
142 {
"verbose", no_argument, NULL,
'v' },
143 {
"help", no_argument, NULL,
'h' },
147 std::vector<double> vec4(4);
148 bool doFlatfield =
false;
149 bool doVigRadial =
false;
150 bool doCropBorders =
true;
151 unsigned verbose = 0;
153 std::string batchPostfix(
"_corr");
154 std::string outputFile;
155 std::string compression;
156 bool doLookupDistortion =
false;
157 bool doLookupTCA =
false;
158 bool doLookupVignetting =
false;
159 std::string cameraMaker;
160 std::string cameraName;
161 std::string lensName;
167 std::string argument;
170 while ((o = getopt_long(argc, argv, optstring, longOptions,
nullptr)) != -1)
174 compression = optarg;
179 if (argument ==
"db")
185 if (sscanf(optarg,
"%lf:%lf:%lf:%lf", &vec4[0], &vec4[1], &vec4[2], &vec4[3]) != 4)
190 srcImg.setRadialDistortionRed(vec4);
196 if (argument ==
"db")
198 doLookupDistortion =
true;
202 if (sscanf(optarg,
"%lf:%lf:%lf:%lf", &vec4[0], &vec4[1], &vec4[2], &vec4[3]) != 4)
207 srcImg.setRadialDistortion(vec4);
213 if (argument ==
"db")
219 if (sscanf(optarg,
"%lf:%lf:%lf:%lf", &vec4[0], &vec4[1], &vec4[2], &vec4[3]) != 4)
224 srcImg.setRadialDistortionBlue(vec4);
228 doCropBorders =
false;
231 srcImg.setFlatfieldFilename(optarg);
235 gamma = atof(optarg);
236 srcImg.setGamma(gamma);
239 cameraMaker = optarg;
248 focalLength = atof(optarg);
251 aperture = atof(optarg);
256 if (argument ==
"db")
258 doLookupVignetting =
true;
262 if (sscanf(optarg,
"%lf:%lf:%lf:%lf", &vec4[0], &vec4[1], &vec4[2], &vec4[3]) != 4)
267 srcImg.setRadialVigCorrCoeff(vec4);
275 std::cout <<
"WARNING: Switch --threads is deprecated. Set environment variable OMP_NUM_THREADS instead" << std::endl;
281 if (sscanf(optarg,
"%lf:%lf", &shiftX, &shiftY) != 2)
291 case LINEAR_RESPONSE:
304 if (doVigRadial && doFlatfield)
322 srcImg.setVigCorrMode(vm);
324 unsigned nFiles = argc - optind;
332 std::vector<std::string> inFiles;
333 std::vector<std::string> outFiles;
336 if (outputFile.length() !=0)
338 inFiles.push_back(std::string(argv[optind]));
339 outFiles.push_back(outputFile);
343 std::string name(argv[optind]);
344 inFiles.push_back(name);
352 bool withExtension =
false;
353 if (outputFile.length() != 0)
355 batchPostfix = outputFile;
356 withExtension = (batchPostfix.find(
'.') != std::string::npos);
358 for (
int i = optind; i < argc; i++)
360 std::string name(argv[i]);
361 inFiles.push_back(name);
373 if (doLookupDistortion || doLookupVignetting || doLookupTCA)
375 if (lensName.empty())
377 if (!cameraMaker.empty() && !cameraName.empty())
379 lensName = cameraMaker;
380 lensName.append(
"|");
381 lensName.append(cameraName);
387 TIFFSetWarningHandler(0);
402 std::vector<std::string>::iterator outIt = outFiles.begin();
403 for (std::vector<std::string>::iterator inIt = inFiles.begin(); inIt != inFiles.end() ; ++inIt, ++outIt)
407 std::cerr <<
"Correcting " << *inIt <<
" -> " << *outIt << endl;
410 currentImg.setFilename(*inIt);
413 vigra::ImageImportInfo
info(inIt->c_str());
414 const char* pixelType =
info.getPixelType();
415 int bands =
info.numBands();
416 int extraBands =
info.numExtraBands();
419 if (doLookupDistortion || doLookupVignetting || doLookupTCA)
422 if (lensName.empty())
426 std::cerr <<
"Not enough data for database lookup" << std::endl
427 <<
"Specify lensname (--lensname) or camera maker and model " << std::endl
428 <<
"(--camera-maker and --camera-model) as parameter." << std::endl;
434 currentImg.setExifLens(lensName);
436 if (fabs(focalLength) < 0.1)
438 if (fabs(currentImg.getExifFocalLength()) < 0.1)
440 std::cerr <<
"Could not determine focal length." << std::endl
441 <<
"Specify focal length (--focallength) as parameter." << std::endl;
447 currentImg.setExifFocalLength(focalLength);
451 std::cout <<
"Lookup in database for " << currentImg.
getDBLensName() <<
" @ " << currentImg.getExifFocalLength() <<
" mm" << std::endl;
453 if (doLookupDistortion)
457 std::cerr <<
"No suitable distortion data found in database." << std::endl
458 <<
"Skipping image." << std::endl;
463 std::vector<double> dist = currentImg.getRadialDistortion();
464 std::cout <<
"Read distortion data: " << dist[0] <<
", " << dist[1] <<
", " << dist[2] <<
", " << dist[3] << std::endl;
467 if (doLookupVignetting)
469 if (fabs(aperture) < 0.1)
471 if (fabs(currentImg.getExifAperture()) < 0.1)
473 std::cerr <<
"Could not determine aperture." << std::endl
474 <<
"Specify aperture (--aperture) as parameter." << std::endl;
480 currentImg.setExifAperture(aperture);
484 std::cerr <<
"No suitable vignetting data found in database." << std::endl
485 <<
"Skipping image." << std::endl;
490 std::vector<double> vig = currentImg.getRadialVigCorrCoeff();
491 std::cout <<
"Read vigneting data: " << vig[1] <<
", " << vig[2] <<
", " << vig[3] << std::endl;
497 std::vector<double> tcaRed, tcaBlue;
498 if (lensDB.
GetTCA(currentImg.
getDBLensName(), currentImg.getExifFocalLength(), tcaRed, tcaBlue))
500 currentImg.setRadialDistortionRed(tcaRed);
501 currentImg.setRadialDistortionBlue(tcaBlue);
505 std::cerr <<
"No suitable tca data found in database." << std::endl
506 <<
"Skipping image." << std::endl;
511 std::cout <<
"Read tca data red: " << tcaRed[0] <<
", " << tcaRed[1] <<
", " << tcaRed[2] <<
", " << tcaRed[3] << std::endl;
512 std::cout <<
"Read tca data blue: " << tcaBlue[0] <<
", " << tcaBlue[1] <<
", " << tcaBlue[2] <<
", " << tcaBlue[3] << std::endl;
518 if (bands == 3 || (bands == 4 && extraBands == 1))
521 if (strcmp(pixelType,
"UINT8") == 0)
523 correctRGB<vigra::RGBValue<vigra::UInt8> >(currentImg,
info, outIt->c_str(), doCropBorders, compression, pdisp);
525 else if (strcmp(pixelType,
"UINT16") == 0)
527 correctRGB<vigra::RGBValue<vigra::UInt16> >(currentImg,
info, outIt->c_str(), doCropBorders, compression, pdisp);
529 else if (strcmp(pixelType,
"INT16") == 0)
531 correctRGB<vigra::RGBValue<vigra::Int16> >(currentImg,
info, outIt->c_str(), doCropBorders, compression, pdisp);
533 else if (strcmp(pixelType,
"UINT32") == 0)
535 correctRGB<vigra::RGBValue<vigra::UInt32> >(currentImg,
info, outIt->c_str(), doCropBorders, compression, pdisp);
537 else if (strcmp(pixelType,
"FLOAT") == 0)
539 correctRGB<vigra::RGBValue<float> >(currentImg,
info, outIt->c_str(), doCropBorders, compression, pdisp);
541 else if (strcmp(pixelType,
"DOUBLE") == 0)
543 correctRGB<vigra::RGBValue<double> >(currentImg,
info, outIt->c_str(), doCropBorders, compression, pdisp);
548 DEBUG_ERROR(
"unsupported depth, only 3 channel images are supported");
551 throw std::runtime_error(
"unsupported depth, only 3 channels images are supported");
556 catch (std::exception& e)
558 std::cerr <<
"caught exception: " << e.what() << std::endl;
585 template <
class SrcImgType,
class AlphaImgType,
class FlatImgType,
class DestImgType>
587 const AlphaImgType& srcAlpha,
588 FlatImgType& srcFlat,
592 DestImgType& destImg,
593 AlphaImgType& destAlpha,
597 typedef typename SrcImgType::value_type SrcPixelType;
598 typedef typename DestImgType::value_type DestPixelType;
612 std::vector<double> outLut;
615 invResp.
setOutput(1.0 /
pow(2.0, src.getExposureValue()), outLut, maxValue);
627 vigra::BImage alpha(destImg.size());
648 vigra::FindAverage<typename FlatImgType::value_type> meanFlat;
660 DEBUG_DEBUG(
"Black border correction scale factor: " << scaleFactor);
661 double sf=scaleFactor;
662 std::vector<double> radGreen = src.getRadialDistortion();
663 for (
int i=0; i < 4; i++)
668 src.setRadialDistortion(radGreen);
684 AlphaImgType redAlpha(destAlpha.size());
687 vigra::copyImage(srcIterRange(srcImg.upperLeft(), srcImg.lowerRight(), vigra::RedAccessor<SrcPixelType>()),
688 destIter(destImg.upperLeft(), vigra::RedAccessor<DestPixelType>()));
695 destIterRange(destImg.upperLeft(), destImg.lowerRight(), vigra::RedAccessor<DestPixelType>()),
707 AlphaImgType greenAlpha(destAlpha.size());
710 vigra::copyImage(srcIterRange(srcImg.upperLeft(), srcImg.lowerRight(), vigra::GreenAccessor<SrcPixelType>()),
711 destIter(destImg.upperLeft(), vigra::GreenAccessor<DestPixelType>()));
716 transformImageAlpha(srcIterRange(srcImg.upperLeft(), srcImg.lowerRight(), vigra::GreenAccessor<SrcPixelType>()),
718 destIterRange(destImg.upperLeft(), destImg.lowerRight(), vigra::GreenAccessor<DestPixelType>()),
730 AlphaImgType blueAlpha(destAlpha.size());
733 vigra::copyImage(srcIterRange(srcImg.upperLeft(), srcImg.lowerRight(), vigra::BlueAccessor<SrcPixelType>()),
734 destIter(destImg.upperLeft(), vigra::BlueAccessor<DestPixelType>()));
740 transformImageAlpha(srcIterRange(srcImg.upperLeft(), srcImg.lowerRight(), vigra::BlueAccessor<SrcPixelType>()),
742 destIterRange(destImg.upperLeft(), destImg.lowerRight(), vigra::BlueAccessor<DestPixelType>()),
752 vigra::functor::Arg1() & vigra::functor::Arg2() & vigra::functor::Arg3());
759 std::vector <double> radCoeff = src.getRadialDistortion();
761 (radCoeff[0] == 0.0 && radCoeff[1] == 0.0 && radCoeff[2] == 0.0 && radCoeff[3] == 1.0))
785 template <
class PIXELTYPE>
789 vigra::BasicImage<vigra::RGBValue<double> > srcImg(info.size());
790 vigra::BasicImage<PIXELTYPE> output(info.size());
791 vigra::BImage alpha(info.size(), 255);
792 vigra::BImage outputAlpha(output.size());
793 if (info.numBands() == 3)
795 vigra::importImage(info,
destImage(srcImg));
801 vigra::FImage flatfield;
804 vigra::ImageImportInfo finfo(src.getFlatfieldFilename().c_str());
805 flatfield.resize(finfo.size());
806 vigra::importImage(finfo,
destImage(flatfield));
809 output, outputAlpha, crop, progress);
810 vigra::ImageExportInfo outInfo(outfile);
811 outInfo.setICCProfile(info.getICCProfile());
812 outInfo.setPixelType(info.getPixelType());
813 if (!compression.empty())
815 outInfo.setCompression(compression.c_str());
817 const std::string filetype(vigra::getEncoder(outInfo.getFileName())->getFileType());
818 if (vigra::isBandNumberSupported(filetype, 4))
821 std::cout <<
"Saving " << outInfo.getFileName() << std::endl;
827 std::cout <<
"Saving " << outInfo.getFileName() <<
" without alpha channel" << std::endl
828 <<
"because the fileformat " << filetype <<
" does not support" << std::endl
829 <<
"an alpha channel." << std::endl;
Dummy progress display, without output.
double getMaxValForPixelType(const std::string &v)
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
static LensDB & GetSingleton()
returns the static LensDB instance
std::string getDBLensName() const
constructs the lens name for the database it is the lensname if known, for compact cameras it is cons...
void enforceMonotonicity(LUT &lut)
enforce monotonicity of an array (mostly used for lookup tables)
void exportImageAlpha(ImageIterator image_upper_left, ImageIterator image_lower_right, ImageAccessor image_accessor, AlphaIterator alpha_upper_left, AlphaAccessor alpha_accessor, const ImageExportInfo &export_info)
Write the image and its alpha channel to a file.
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
static void Clean()
cleanup the static LensDB instance, must be called at the end of the program
class to access Hugins camera and lens database
empirical model of response
VignettingCorrMode
vignetting correction mode (bitflags, no real enum)
void combineThreeImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, SrcImageIterator3 src3_upperleft, SrcAccessor3 src3_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
std::string getExtension(const std::string &basename2)
Get extension of a filename.
bool getCorrectTCA() const
void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor, VigraTrueType)
float pow(float a, double b)
radial vignetting correction
void correctImage(SrcImgType &srcImg, const AlphaImgType &srcAlpha, const FlatImgType &srcFlat, HuginBase::SrcPanoImage src, vigra_ext::Interpolator interpolator, double maxValue, DestImgType &destImg, AlphaImgType &destAlpha, bool doCrop, AppBase::ProgressDisplay *progress)
void setMessage(const std::string &message, const std::string &filename="")
sets the message to given string
std::string stripExtension(const std::string &basename2)
remove extension of a filename
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
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
void createEMoRLUT(const std::vector< float > ¶ms, VECTOR &lut)
bool readVignettingFromDB()
tries to read vignetting data from lens database you need to call SrcPanoImage::readEXIF before to fi...
void transformImageAlpha(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, std::pair< SrcAlphaIterator, SrcAlphaAccessor > srcAlpha, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform image, and respect a possible alpha channel.
bool readDistortionFromDB()
tries to read distortion data from lens database you need to call SrcPanoImage::readEXIF before to fi...
bool readEXIF()
try to fill out information about the image, by examining the exif data
void setSize(vigra::Size2D val)
Set the image size in pixels.
std::string GetHuginVersion()
return a string with version numbers
double estScaleFactorForFullFrame(const SrcPanoImage &src)
Calculate effective scaling factor for a given source image.
vigra::triple< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImageRange(ROIImage< Image, Alpha > &img)
void copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
static void info(const char *fmt,...)
bool GetTCA(const std::string &lens, const double focal, std::vector< double > &tca_red, std::vector< double > &tca_blue) const
returns the tca distortion parameters of the lens
void correctRGB(HuginBase::SrcPanoImage &src, vigra::ImageImportInfo &info, const char *outfile, bool crop, const std::string &compression, AppBase::ProgressDisplay *progress)
Interpolator
enum with all interpolation methods
All variables of a source image.
a progress display to print progress reports to a stream
std::string tolower(const std::string &s)
convert a string to lowercase
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
int main(int argc, char *argv[])