Skip to content

Commit

Permalink
US33: Add alignment switch
Browse files Browse the repository at this point in the history
- Create two classes with options to load and save an ImageStack. The idea is that, in the GUI, dialogs extend these classes.
- Add a method to ImageStack to save the image, instead of calling DngWriter directly.
- Add a command line switch to disable auto-alignment.
  • Loading branch information
jcelaya committed May 12, 2014
1 parent df4e297 commit 45f8a5e
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 86 deletions.
28 changes: 20 additions & 8 deletions ImageStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <algorithm>
#include "ImageStack.hpp"
#include "DngWriter.hpp"
using namespace std;
using namespace hdrmerge;

Expand All @@ -41,10 +42,10 @@ bool ImageStack::addImage(std::unique_ptr<Image> & i) {
}


int ImageStack::load(const std::list<std::string> & fileNames, ProgressIndicator & progress) {
int step = 100 / (fileNames.size() + 1);
int ImageStack::load(const LoadOptions & options, ProgressIndicator & progress) {
int step = 100 / (options.fileNames.size() + 1);
int p = -step;
for (auto & name : fileNames) {
for (auto & name : options.fileNames) {
progress.advance(p += step, "Loading %1", name.c_str());
std::unique_ptr<Image> image(new Image(name.c_str()));
if (image.get() == nullptr || !image->good()) {
Expand All @@ -54,15 +55,26 @@ int ImageStack::load(const std::list<std::string> & fileNames, ProgressIndicator
return 2;
}
}
progress.advance(p += step, "Aligning");
align();
if (options.align) {
progress.advance(p += step, "Aligning");
align();
}
computeRelExposures();
imageIndex.generateFrom(*this);
progress.advance(p += step, "Done loading!");
progress.advance(100, "Done loading!");
return 0;
}


int ImageStack::save(const SaveOptions & options, ProgressIndicator & progress) {
DngWriter writer(*this, progress);
writer.setBitsPerSample(options.bps);
writer.setPreviewWidth(options.previewSize);
writer.setIndexFileName(options.maskFileName.c_str());
writer.write(options.fileName);
}


void ImageStack::align() {
if (images.size() > 1) {
int dx = 0, dy = 0;
Expand Down Expand Up @@ -147,12 +159,12 @@ void ImageStack::compose(float * dst) const {

string ImageStack::buildOutputFileName() const {
string name;
std::list<string> names;
std::vector<string> names;
for (auto & image : images) {
const string & f = image->getMetaData().fileName;
names.push_back(f.substr(0, f.find_last_of('.')));
}
names.sort();
sort(names.begin(), names.end());
if (names.size() > 1) {
string & last = names.back();
int pos = last.length() - 1;
Expand Down
21 changes: 19 additions & 2 deletions ImageStack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#define _IMAGESTACK_H_

#include <vector>
#include <list>
#include <string>
#include <memory>
#include "Image.hpp"
Expand All @@ -34,11 +33,29 @@

namespace hdrmerge {


struct LoadOptions {
std::vector<std::string> fileNames;
bool align;
LoadOptions() : align(true) {}
};


struct SaveOptions {
int bps;
int previewSize;
std::string fileName;
std::string maskFileName;
SaveOptions() : bps(16), previewSize(0) {}
};


class ImageStack {
public:
ImageStack() : width(0), height(0) {}

int load(const std::list<std::string> & fileNames, ProgressIndicator & progress);
int load(const LoadOptions & options, ProgressIndicator & progress);
int save(const SaveOptions & options, ProgressIndicator & progress);
void align();
void computeRelExposures();
void generateImageIndex() {
Expand Down
66 changes: 29 additions & 37 deletions Launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,17 @@
#include <QLocale>
#include "Launcher.hpp"
#include "MainWindow.hpp"
#include "ImageStack.hpp"
#include "DngWriter.hpp"
using namespace std;

namespace hdrmerge {

Launcher::Launcher() : outFileName(nullptr), maskFileName(nullptr),
automatic(false), help(false), bps(16), previewWidth(nullptr) {}
Launcher::Launcher() : automatic(false), help(false), previewWidth(nullptr) {}


int Launcher::startGUI() {
// Create main window
MainWindow mw;
mw.preload(inFileNames);
mw.preload(&options);
mw.show();

return QApplication::exec();
Expand Down Expand Up @@ -79,39 +76,31 @@ class CoutProgressIndicator : public ProgressIndicator {
int Launcher::automaticMerge() {
CoutProgressIndicator pi;
ImageStack stack;
if (stack.load(inFileNames, pi)) {
int i = pi.getPercent() * (inFileNames.size() + 1) / 100;
for (auto & name : inFileNames) {
if (!i--) {
cout << QCoreApplication::translate("LoadSave", "Error loading %1").arg(name.c_str()) << endl;
return 1;
}
if (stack.load(options, pi)) {
int i = pi.getPercent() * (options.fileNames.size() + 1) / 100;
if (i) {
cout << QCoreApplication::translate("LoadSave", "Error loading %1").arg(options.fileNames[i].c_str()) << endl;
return 1;
}
}
string fileName;
if (outFileName != NULL) {
fileName = outFileName;
size_t extPos = fileName.find_last_of('.');
if (extPos > fileName.length() || fileName.substr(extPos) != ".dng") {
fileName += ".dng";
if (!wOptions.fileName.empty()) {
size_t extPos = wOptions.fileName.find_last_of('.');
if (extPos > wOptions.fileName.length() || wOptions.fileName.substr(extPos) != ".dng") {
wOptions.fileName += ".dng";
}
} else {
fileName = stack.buildOutputFileName() + ".dng";
wOptions.fileName = stack.buildOutputFileName() + ".dng";
}
cout << QCoreApplication::translate("LoadSave", "Writing result to %1").arg(fileName.c_str()) << endl;
DngWriter writer(stack, pi);
writer.setBitsPerSample(bps);
size_t pw = stack.getWidth();
cout << QCoreApplication::translate("LoadSave", "Writing result to %1").arg(wOptions.fileName.c_str()) << endl;
wOptions.previewSize = stack.getWidth();
if (previewWidth != nullptr) {
if (string("half") == previewWidth) {
pw /= 2;
wOptions.previewSize /= 2;
} else if (string("none") == previewWidth) {
pw = 0;
wOptions.previewSize = 0;
}
}
writer.setPreviewWidth(pw);
writer.setIndexFileName(maskFileName);
writer.write(fileName);
stack.save(wOptions, pi);
return 0;
}

Expand All @@ -122,51 +111,54 @@ void Launcher::parseCommandLine(int argc, char * argv[]) {
for (int i = 1; i < argc; ++i) {
if (string("-o") == argv[i]) {
if (++i < argc) {
outFileName = argv[i];
wOptions.fileName = argv[i];
automatic = true;
}
} else if (string("-m") == argv[i]) {
if (++i < argc) {
maskFileName = argv[i];
wOptions.maskFileName = argv[i];
}
} else if (string("-a") == argv[i]) {
automatic = true;
} else if (string("-n") == argv[i] || string("--no-align") == argv[i]) {
options.align = false;
} else if (string("--help") == argv[i]) {
help = true;
} else if (string("-b") == argv[i]) {
if (++i < argc) {
int value = stoi(argv[i]);
if (value == 32 || value == 24 || value == 16) bps = value;
if (value == 32 || value == 24 || value == 16) wOptions.bps = value;
}
} else if (string("-p") == argv[i]) {
if (++i < argc) {
previewWidth = argv[i];
}
} else if (argv[i][0] != '-') {
inFileNames.push_back(argv[i]);
options.fileNames.push_back(argv[i]);
}
}
}


void Launcher::showHelp() {
auto tr = [&] (const char * text) { return QCoreApplication::translate("Help", text); };
cout << tr("Usage") << ": HDRMerge [--help] [-o OUT_FILE] [-a] [-b BPS] [-p full|half|none] [-m MASK_FILE] [RAW_FILES ...]" << endl;
cout << tr("Merges RAW_FILES into OUT_FILE, to obtain an HDR raw image.") << endl;
cout << tr("Usage") << ": HDRMerge [--help] [OPTIONS ...] [RAW_FILES ...]" << endl;
cout << tr("Merges RAW_FILES into an HDR DNG raw image.") << endl;
cout << endl;
cout << tr("Options:") << endl;
cout << " " << "--help " << tr("Shows this message.") << endl;
cout << " " << "-o OUT_FILE " << tr("Sets OUT_FILE as the output file name.") << endl;
cout << " " << "-a " << tr("Calculates the output file name automatically. Ignores -o.") << endl;
cout << " " << "-b BPS " << tr("Bits per sample, can be 16, 24 or 32.") << endl;
cout << " " << "-p full|half|none " << tr("Preview width.") << endl;
cout << " " << "-m MASK_FILE " << tr("Saves the mask to MASK_FILE as a PNG image.") << endl;
cout << " " << "-n|--no-align " << tr("Do not auto-align source images.") << endl;
cout << " " << "-o OUT_FILE " << tr("Sets OUT_FILE as the output file name.") << endl;
cout << " " << "-p full|half|none " << tr("Preview width.") << endl;
cout << " " << "RAW_FILES " << tr("The input raw files.") << endl;
}


int Launcher::run() {
bool useGUI = !help && (!automatic || inFileNames.empty());
bool useGUI = !help && (!automatic || options.fileNames.empty());

QApplication app(argcGUI, argvGUI, useGUI);

Expand Down
7 changes: 3 additions & 4 deletions Launcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include <list>
#include <string>
#include "ImageStack.hpp"

namespace hdrmerge {

Expand All @@ -43,12 +44,10 @@ class Launcher {

int argcGUI;
char ** argvGUI;
std::list<std::string> inFileNames;
char * outFileName;
char * maskFileName;
LoadOptions options;
SaveOptions wOptions;
bool automatic;
bool help;
int bps;
char * previewWidth;
};

Expand Down
52 changes: 23 additions & 29 deletions MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,16 +246,10 @@ void MainWindow::about() {
}


void MainWindow::preload(const list<string> & fileNames) {
for (auto & name : fileNames)
preLoadFiles << QString::fromLocal8Bit(name.c_str());
}


void MainWindow::showEvent(QShowEvent * event) {
if (!preLoadFiles.empty()) {
loadImages(preLoadFiles);
preLoadFiles.clear();
if (preloadOptions && !preloadOptions->fileNames.empty()) {
loadImages(*preloadOptions);
preloadOptions = nullptr;
}
}

Expand Down Expand Up @@ -289,31 +283,32 @@ void MainWindow::loadImages() {
QString lastDir = QDir(files.front()).absolutePath();
lastDir.truncate(lastDir.lastIndexOf('/'));
settings.setValue("lastOpenDirectory", lastDir);
loadImages(files);
LoadOptions options;
unsigned int numImages = files.size();
for (int i = 0; i < numImages; ++i) {
options.fileNames.push_back(QDir::toNativeSeparators(files[i]).toLocal8Bit().constData());
}
loadImages(options);
}
shiftPressed = controlPressed = false;
dragToolAction->trigger();
}


void MainWindow::loadImages(const QStringList & files) {
if (!files.empty()) {
unsigned int numImages = files.size();
list<string> inFileNames;
for (int i = 0; i < numImages; ++i) {
inFileNames.push_back(QDir::toNativeSeparators(files[i]).toLocal8Bit().constData());
}
void MainWindow::loadImages(const LoadOptions & options) {
if (!options.fileNames.empty()) {
unsigned int numImages = options.fileNames.size();
ImageStack * newImages = new ImageStack();
ProgressDialog progress(this);
progress.setWindowTitle(tr("Open raw images"));
QFuture<int> error = QtConcurrent::run([&] () { return newImages->load(inFileNames, progress); });
QFuture<int> error = QtConcurrent::run([&] () { return newImages->load(options, progress); });
while (error.isRunning())
QApplication::instance()->processEvents(QEventLoop::ExcludeUserInputEvents);
if (error.result()) {
int i = progress.getPercent() * (numImages + 1) / 100;
QString message = error.result() == 1 ?
tr("Unable to open file %1.").arg(files[i]) :
tr("File %1 has not the same format as the previous ones.").arg(files[i]);
tr("Unable to open file %1.").arg(options.fileNames[i].c_str()) :
tr("File %1 has not the same format as the previous ones.").arg(options.fileNames[i].c_str());
QMessageBox::warning(this, tr("Error opening file"), message);
delete newImages;
return;
Expand Down Expand Up @@ -369,17 +364,16 @@ void MainWindow::saveResult() {
ProgressDialog pd(this);
pd.setWindowTitle(tr("Save DNG file"));
QFuture<void> result = QtConcurrent::run([&]() {
DngWriter writer(*images, pd);
writer.setBitsPerSample(dpd.getBps());
size_t previewWidth;
SaveOptions options;
options.bps = dpd.getBps();
options.maskFileName = dpd.getIndexFileName().toUtf8().constData();
options.fileName = fileName;
switch (dpd.getPreviewSize()) {
case 0: previewWidth = images->getWidth(); break;
case 1: previewWidth = images->getWidth() / 2; break;
default: previewWidth = 0;
case 0: options.previewSize = images->getWidth(); break;
case 1: options.previewSize = images->getWidth() / 2; break;
default: options.previewSize = 0;
}
writer.setPreviewWidth(previewWidth);
writer.setIndexFileName(dpd.getIndexFileName());
writer.write(fileName);
images->save(options, pd);
});
while (result.isRunning())
QApplication::instance()->processEvents(QEventLoop::ExcludeUserInputEvents);
Expand Down
Loading

0 comments on commit 45f8a5e

Please sign in to comment.