New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extreme dynamic scene loses most details #113
Comments
Would be good to first exclude Magic Lantern as the source of the issues. I've never processed Magic Lantern raws in HDRMerge. Could you try the maximum bracketing set allowed by standard Canon firmware? I renamed the files in order of EV, 1-8.cr2. Worth nothing that
Fixing the aperture made no difference:
|
The Canon EOS 600D can only do three bracketed shots (max. +-2EV) with stock firmware. That's why I use ML. There's nothing special about the CR2s (apart from not having the bracketing sequence tag AFAIK), as ML only changes the shutter time the way I would do manually. So you can take the center three images and basically get what Canon bracketing would have shot. Worth to mention, I never had a problem with ML auto bracketed shots, but this here is extreme in it's dynamic range. Most of my bracketed scenes causes four to five shots 2EV apart.
About the three sets: The longest exposure might be longer than the default set gap of HDRMerge. Plus, I had to take the series twice, because someone was walking through the shot. Maybe I made a best-of selection, can't remember and don't have the full series here to check. About BL and WB: I can't tell you anything about BL, but WB was set to AutoWB because I wasn't aware that this would matter. Does it?
This was shot with my manual Samyang 35 1.4, most probably at f/2 or maybe f/2.8. I had no problems with the combination of this camera, lens, and ML auto bracketing in the past, but the scenes weren't that extreme. HTH, |
@Beep6581 In your batch run, why is the 10s exposure (1/0.1) still rated -2.67807EV? Is that normal? I would have expected at least 0 for the brightest image. |
@Floessie I'm "under the impression" that it doesn't matter as long as the step size is right, but I will test both the EV and WB on some other shots this weekend. |
@Beep6581 @heckflosse Any idea where to look? Could it be some cancelation problem like the one Alberto fixed for Fattal? I'd really like to develop that shot... Best, |
@Floessie I have an idea where to look. Have to check that. I will tell you later today. |
@heckflosse The problem is the darkest image. diff --git a/src/Image.cpp b/src/Image.cpp
index da5dfec..5fea6e7 100644
--- a/src/Image.cpp
+++ b/src/Image.cpp
@@ -153,6 +153,7 @@ void Image::computeResponseFunction(const Image & r) {
alglib::spline1dfitreport rep;
alglib::spline1dfitpenalized(values, adjValues, i, 200, 3, info, response.nonLinear, rep);
response.linear = alglib::spline1dcalc(response.nonLinear, response.threshold) / response.threshold;
+ printf("algo 1: %lf\n", response.linear);
} else {
response.threshold = 65535;
// Fallback method for dark images:
@@ -171,6 +172,7 @@ void Image::computeResponseFunction(const Image & r) {
}
}
response.linear = numerator / denom;
+ printf("algo 2: %lf\n", response.linear);
}
} With the whole set I get:
And most of the resulting image is black. Without
I tried this and that inside |
@Floessie is this still reproducible? |
@Beep6581 Last time I checked, it was still reproducible and there were no changes concerning |
@Beep6581 Problem still exists. Range is 0, 4, 6, 8, ..., 16 EV. |
@Floessie could you re-upload the images if you still have them :) ? |
@Floessie Could you try this patch? It enhances the way the response function parameters are calucalted for dark images: index 4b1df64..a077b46 100644
--- a/src/Image.cpp
+++ b/src/Image.cpp
@@ -165,7 +165,7 @@ void Image::computeResponseFunction(const Image & r) {
int pos = y * width + x;
double v = usePixels[pos];
double nv = rUsePixels[pos];
- if (v >= nv && v < satThreshold) {
+ if (v >= nv && v < response.threshold) {
numerator += v * r.response(nv);
denom += v * v;
} Reasoning: This code lives in an else block that handles dark images, the changes: the |
I did some testing on a normal set of images set Before the patch: There is an artifacts near the clouds, it is the same as #162 (comment) At the same time it fixes the data-loss issue with your images. |
@fanckush there seem to be quite a few issues with that image. The flag and water artifacts could be explained by movement between the images (I haven't checked the raw photo) but other areas are static. |
@fanckush As much as I liked this being solved, your patch is not yet on the spot. With patchWithout patch |
@Floessie yup. It was a naive attempt that showed good results for one case. I'm still going through understanding what everything does and only after that I can attempt to solve this :) this may take a while, I'll came back with another patch 👍 |
I'm back with another patch. First I wanna say that this is much trickier than I expected. TL;DR at the bottom. @Floessie you are getting 1MB because most of the resulting HDR image is dark AND the small bright part is extremely bright. Part 1 ImageStack::compose() Part 2 TL;DR Patch I am here!diff --git a/src/Image.cpp b/src/Image.cpp
index 4b1df64..838071b 100644
--- a/src/Image.cpp
+++ b/src/Image.cpp
@@ -44,16 +44,19 @@ void Image::buildImage(uint16_t * rawImage, const RawParameters & params) {
size_t size = width*height;
brightness = 0.0;
max = 0;
+ min = params.black * 100; // some inital big value
for (size_t y = 0, ry = params.topMargin; y < height; ++y, ++ry) {
for (size_t x = 0, rx = params.leftMargin; x < width; ++x, ++rx) {
uint16_t v = rawImage[ry*params.rawWidth + rx];
(*this)(x, y) = v;
brightness += v;
if (v > max) max = v;
+ if (v < min) min = v;
}
}
brightness /= size;
- response.setLinear(params.max == 0 ? 1.0 : 65535.0 / params.max);
+ // response.setLinear(params.max == 0 ? 1.0 : 65535.0 / params.max);
+ response.setLinear(1.0);
subtractBlack(params);
}
@@ -165,9 +168,11 @@ void Image::computeResponseFunction(const Image & r) {
int pos = y * width + x;
double v = usePixels[pos];
double nv = rUsePixels[pos];
- if (v >= nv && v < satThreshold) {
- numerator += v * r.response(nv);
- denom += v * v;
+ if (v <= nv && nv < satThreshold) {
+ if ((nv - (double)r.min) / ((double)r.max - (double)r.min) > 0.1) {
+ numerator += v * r.response(nv);
+ denom += v * v;
+ }
}
}
}
diff --git a/src/Image.hpp b/src/Image.hpp
index 0bc760b..f0f4ff5 100644
--- a/src/Image.hpp
+++ b/src/Image.hpp
@@ -104,7 +104,7 @@ private:
QString filename;
std::unique_ptr<Array2D<uint16_t>[]> scaled;
- uint16_t satThreshold, max;
+ uint16_t satThreshold, max, min;
double brightness;
ResponseFunction response;
double halfLightPercent;
diff --git a/src/ImageIO.cpp b/src/ImageIO.cpp
index a80ca51..3dbdfa3 100644
--- a/src/ImageIO.cpp
+++ b/src/ImageIO.cpp
@@ -183,7 +183,7 @@ void ImageIO::save(const SaveOptions & options, ProgressIndicator & progress) {
params.width = stack.getWidth();
params.height = stack.getHeight();
params.adjustWhite(stack.getImage(stack.size() - 1));
- Array2D<float> composedImage = stack.compose(params, options.featherRadius);
+ Array2D<float> composedImage = stack.compose(params, options.featherRadius, options.bps);
progress.advance(33, "Rendering preview");
QImage preview = renderPreview(composedImage, params, stack.getMaxExposure(), options.previewSize <= 1);
diff --git a/src/ImageStack.cpp b/src/ImageStack.cpp
index 6074321..5090f10 100644
--- a/src/ImageStack.cpp
+++ b/src/ImageStack.cpp
@@ -21,6 +21,7 @@
*/
#include <algorithm>
+#include <cmath>
#include "BoxBlur.hpp"
#include "ImageStack.hpp"
@@ -166,8 +167,8 @@ void ImageStack::crop() {
void ImageStack::computeResponseFunctions() {
Timer t("Compute response functions");
- for (int i = images.size() - 2; i >= 0; --i) {
- images[i].computeResponseFunction(images[i + 1]);
+ for (int i = 1; i < images.size(); ++i) {
+ images[i].computeResponseFunction(images[i - 1]);
}
}
@@ -394,7 +395,7 @@ static Array2D<uint8_t> fattenMask(const Array2D<uint8_t> & mask, int radius) {
}
#endif
-Array2D<float> ImageStack::compose(const RawParameters & params, int featherRadius) const {
+Array2D<float> ImageStack::compose(const RawParameters & params, int featherRadius, int bit_depth) const {
int imageMax = images.size() - 1;
BoxBlur map(fattenMask(mask, featherRadius));
measureTime("Blur", [&] () {
@@ -459,7 +460,10 @@ Array2D<float> ImageStack::compose(const RawParameters & params, int featherRadi
dst.displace(params.leftMargin, params.topMargin);
// Scale to params.max and recover the black levels
- float mult = (params.max - params.maxBlack) / max;
+ int max_possible = std::pow(2.0, bit_depth); // almost always 65536
+ int max_black = params.maxBlack * ((double)max_possible / (double)params.max);
+ float mult = (max_possible - max_black) / max; // scale dst to fit the bit depth space
+
#pragma omp parallel for
for (size_t y = 0; y < params.rawHeight; ++y) {
for (size_t x = 0; x < params.rawWidth; ++x) {
diff --git a/src/ImageStack.hpp b/src/ImageStack.hpp
index 87610da..a16c63a 100644
--- a/src/ImageStack.hpp
+++ b/src/ImageStack.hpp
@@ -48,7 +48,7 @@ public:
void crop();
void computeResponseFunctions();
void generateMask();
- Array2D<float> compose(const RawParameters & md, int featherRadius) const;
+ Array2D<float> compose(const RawParameters & md, int featherRadius, int bit_depth) const;
size_t size() const { return images.size(); }
|
@fanckush I'd like to try your patch, but it doesn't apply on |
if this works, I will work on making it less patchy for example: if ((nv - (double)r.min) / ((double)r.max - (double)r.min) > 0.1) { is not stable in the case of a dark stack such as #173. The result HDR image is actually simply black because the |
There was a rejected hunk against ImageStack where I made different changes for jcelaya#216 DO NOT MERGE/CHERRY-PICK - commit authorship is wrong and needs to be fixed!
Hi Ingo and DrSlony,
I've shot a scene using Magic Lantern's auto HDR bracketing, which correctly resulted in eight shots all 2EV apart. I naively saved them to a 16b DNG, knowing the embedded preview would be crap. Only later, when I struggled developing the DNG in RT, I learned that this DNG was only 1MB in size. 😮
Then I saved the series in 32b, this resulted in a 16MB DNG, again undevelopable. Only when I reduce the set to the "innermost" three to four shots I get a decent DNG.
What's wrong with the scene, and why does HDRMerge lose that data instead of accumulating all possible data into the DNG?
You can find the files here.
Best,
Flössie
The text was updated successfully, but these errors were encountered: