merge with dev
This commit is contained in:
@@ -164,7 +164,7 @@ endif()
|
|||||||
if(NOT DEFINED DATADIR)
|
if(NOT DEFINED DATADIR)
|
||||||
if(BUILD_BUNDLE)
|
if(BUILD_BUNDLE)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(DATADIR "../Resources")
|
set(DATADIR "../../Resources")
|
||||||
else()
|
else()
|
||||||
set(DATADIR .)
|
set(DATADIR .)
|
||||||
endif()
|
endif()
|
||||||
@@ -176,7 +176,7 @@ endif()
|
|||||||
if(NOT DEFINED LIBDIR)
|
if(NOT DEFINED LIBDIR)
|
||||||
if(BUILD_BUNDLE)
|
if(BUILD_BUNDLE)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(LIBDIR "../Frameworks")
|
set(LIBDIR "../../Frameworks")
|
||||||
else()
|
else()
|
||||||
set(LIBDIR .)
|
set(LIBDIR .)
|
||||||
endif()
|
endif()
|
||||||
@@ -256,6 +256,10 @@ if(APPLE)
|
|||||||
if("${CODESIGNID}")
|
if("${CODESIGNID}")
|
||||||
set(CODESIGNID "${CODESIGNID}" CACHE STRING "Codesigning Identity")
|
set(CODESIGNID "${CODESIGNID}" CACHE STRING "Codesigning Identity")
|
||||||
endif()
|
endif()
|
||||||
|
if("${NOTARY}")
|
||||||
|
set(NOTARY "${NOTARY}" CACHE STRING "Notarization Identity")
|
||||||
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Enforce absolute paths for non-bundle builds:
|
# Enforce absolute paths for non-bundle builds:
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ link_directories("${PROJECT_SOURCE_DIR}/rtexif"
|
|||||||
set(CAMCONSTSFILE "camconst.json")
|
set(CAMCONSTSFILE "camconst.json")
|
||||||
|
|
||||||
set(RTENGINESOURCEFILES
|
set(RTENGINESOURCEFILES
|
||||||
|
badpixels.cc
|
||||||
CA_correct_RT.cc
|
CA_correct_RT.cc
|
||||||
EdgePreservingDecomposition.cc
|
EdgePreservingDecomposition.cc
|
||||||
FTblockDN.cc
|
FTblockDN.cc
|
||||||
|
|||||||
582
rtengine/badpixels.cc
Normal file
582
rtengine/badpixels.cc
Normal file
@@ -0,0 +1,582 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RawTherapee.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2019 Gabor Horvath <hgabor@rawtherapee.com>
|
||||||
|
*
|
||||||
|
* RawTherapee is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* RawTherapee is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "array2D.h"
|
||||||
|
#include "median.h"
|
||||||
|
#include "pixelsmap.h"
|
||||||
|
#include "rawimagesource.h"
|
||||||
|
|
||||||
|
namespace rtengine
|
||||||
|
{
|
||||||
|
|
||||||
|
/* interpolateBadPixelsBayer: correct raw pixels looking at the bitmap
|
||||||
|
* takes into consideration if there are multiple bad pixels in the neighborhood
|
||||||
|
*/
|
||||||
|
int RawImageSource::interpolateBadPixelsBayer(const PixelsMap &bitmapBads, array2D<float> &rawData)
|
||||||
|
{
|
||||||
|
constexpr float eps = 1.f;
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#pragma omp parallel for reduction(+:counter) schedule(dynamic,16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int row = 2; row < H - 2; ++row) {
|
||||||
|
for (int col = 2; col < W - 2; ++col) {
|
||||||
|
const int sk = bitmapBads.skipIfZero(col, row); //optimization for a stripe all zero
|
||||||
|
|
||||||
|
if (sk) {
|
||||||
|
col += sk - 1; //-1 is because of col++ in cycle
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bitmapBads.get(col, row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float wtdsum = 0.f, norm = 0.f;
|
||||||
|
|
||||||
|
// diagonal interpolation
|
||||||
|
if (FC(row, col) == 1) {
|
||||||
|
// green channel. We can use closer pixels than for red or blue channel. Distance to center pixel is sqrt(2) => weighting is 0.70710678
|
||||||
|
// For green channel following pixels will be used for interpolation. Pixel to be interpolated is in center.
|
||||||
|
// 1 means that pixel is used in this step, if itself and his counterpart are not marked bad
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// 0 1 0 1 0
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// 0 1 0 1 0
|
||||||
|
// 0 0 0 0 0
|
||||||
|
for (int dx = -1; dx <= 1; dx += 2) {
|
||||||
|
if (bitmapBads.get(col + dx, row - 1) || bitmapBads.get(col - dx, row + 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float dirwt = 0.70710678f / (fabsf(rawData[row - 1][col + dx] - rawData[row + 1][col - dx]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row - 1][col + dx] + rawData[row + 1][col - dx]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// red and blue channel. Distance to center pixel is sqrt(8) => weighting is 0.35355339
|
||||||
|
// For red and blue channel following pixels will be used for interpolation. Pixel to be interpolated is in center.
|
||||||
|
// 1 means that pixel is used in this step, if itself and his counterpart are not marked bad
|
||||||
|
// 1 0 0 0 1
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// 1 0 0 0 1
|
||||||
|
for (int dx = -2; dx <= 2; dx += 4) {
|
||||||
|
if (bitmapBads.get(col + dx, row - 2) || bitmapBads.get(col - dx, row + 2)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float dirwt = 0.35355339f / (fabsf(rawData[row - 2][col + dx] - rawData[row + 2][col - dx]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row - 2][col + dx] + rawData[row + 2][col - dx]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// channel independent. Distance to center pixel is 2 => weighting is 0.5
|
||||||
|
// Additionally for all channel following pixels will be used for interpolation. Pixel to be interpolated is in center.
|
||||||
|
// 1 means that pixel is used in this step, if itself and his counterpart are not marked bad
|
||||||
|
// 0 0 1 0 0
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// 1 0 0 0 1
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// 0 0 1 0 0
|
||||||
|
|
||||||
|
// horizontal interpolation
|
||||||
|
if (!(bitmapBads.get(col - 2, row) || bitmapBads.get(col + 2, row))) {
|
||||||
|
const float dirwt = 0.5f / (fabsf(rawData[row][col - 2] - rawData[row][col + 2]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row][col - 2] + rawData[row][col + 2]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertical interpolation
|
||||||
|
if (!(bitmapBads.get(col, row - 2) || bitmapBads.get(col, row + 2))) {
|
||||||
|
const float dirwt = 0.5f / (fabsf(rawData[row - 2][col] - rawData[row + 2][col]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row - 2][col] + rawData[row + 2][col]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LIKELY(norm > 0.f)) { // This means, we found at least one pair of valid pixels in the steps above, likelihood of this case is about 99.999%
|
||||||
|
rawData[row][col] = wtdsum / (2.f * norm); //gradient weighted average, Factor of 2.f is an optimization to avoid multiplications in former steps
|
||||||
|
counter++;
|
||||||
|
} else { //backup plan -- simple average. Same method for all channels. We could improve this, but it's really unlikely that this case happens
|
||||||
|
int tot = 0;
|
||||||
|
float sum = 0.f;
|
||||||
|
|
||||||
|
for (int dy = -2; dy <= 2; dy += 2) {
|
||||||
|
for (int dx = -2; dx <= 2; dx += 2) {
|
||||||
|
if (bitmapBads.get(col + dx, row + dy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += rawData[row + dy][col + dx];
|
||||||
|
tot++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tot > 0) {
|
||||||
|
rawData[row][col] = sum / tot;
|
||||||
|
counter ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter; // Number of interpolated pixels.
|
||||||
|
}
|
||||||
|
|
||||||
|
/* interpolateBadPixelsNcolors: correct raw pixels looking at the bitmap
|
||||||
|
* takes into consideration if there are multiple bad pixels in the neighborhood
|
||||||
|
*/
|
||||||
|
int RawImageSource::interpolateBadPixelsNColours(const PixelsMap &bitmapBads, const int colors)
|
||||||
|
{
|
||||||
|
constexpr float eps = 1.f;
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#pragma omp parallel for reduction(+:counter) schedule(dynamic,16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int row = 2; row < H - 2; ++row) {
|
||||||
|
for (int col = 2; col < W - 2; ++col) {
|
||||||
|
const int sk = bitmapBads.skipIfZero(col, row); //optimization for a stripe all zero
|
||||||
|
|
||||||
|
if (sk) {
|
||||||
|
col += sk - 1; //-1 is because of col++ in cycle
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bitmapBads.get(col, row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float wtdsum[colors];
|
||||||
|
float norm[colors];
|
||||||
|
for (int c = 0; c < colors; ++c) {
|
||||||
|
wtdsum[c] = norm[c] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// diagonal interpolation
|
||||||
|
for (int dx = -1; dx <= 1; dx += 2) {
|
||||||
|
if (bitmapBads.get(col + dx, row - 1) || bitmapBads.get(col - dx, row + 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int c = 0; c < colors; ++c) {
|
||||||
|
const float dirwt = 0.70710678f / (fabsf(rawData[row - 1][(col + dx) * colors + c] - rawData[row + 1][(col - dx) * colors + c]) + eps);
|
||||||
|
wtdsum[c] += dirwt * (rawData[row - 1][(col + dx) * colors + c] + rawData[row + 1][(col - dx) * colors + c]);
|
||||||
|
norm[c] += dirwt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// horizontal interpolation
|
||||||
|
if (!(bitmapBads.get(col - 1, row) || bitmapBads.get(col + 1, row))) {
|
||||||
|
for (int c = 0; c < colors; ++c) {
|
||||||
|
const float dirwt = 1.f / (fabsf(rawData[row][(col - 1) * colors + c] - rawData[row][(col + 1) * colors + c]) + eps);
|
||||||
|
wtdsum[c] += dirwt * (rawData[row][(col - 1) * colors + c] + rawData[row][(col + 1) * colors + c]);
|
||||||
|
norm[c] += dirwt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertical interpolation
|
||||||
|
if (!(bitmapBads.get(col, row - 1) || bitmapBads.get(col, row + 1))) {
|
||||||
|
for (int c = 0; c < colors; ++c) {
|
||||||
|
const float dirwt = 1.f / (fabsf(rawData[row - 1][col * colors + c] - rawData[row + 1][col * colors + c]) + eps);
|
||||||
|
wtdsum[c] += dirwt * (rawData[row - 1][col * colors + c] + rawData[row + 1][col * colors + c]);
|
||||||
|
norm[c] += dirwt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LIKELY(norm[0] > 0.f)) { // This means, we found at least one pair of valid pixels in the steps above, likelihood of this case is about 99.999%
|
||||||
|
for (int c = 0; c < colors; ++c) {
|
||||||
|
rawData[row][col * colors + c] = wtdsum[c] / (2.f * norm[c]); //gradient weighted average, Factor of 2.f is an optimization to avoid multiplications in former steps
|
||||||
|
}
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
} else { //backup plan -- simple average. Same method for all channels. We could improve this, but it's really unlikely that this case happens
|
||||||
|
int tot = 0;
|
||||||
|
float sum[colors];
|
||||||
|
for (int c = 0; c < colors; ++c) {
|
||||||
|
sum[c] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int dy = -2; dy <= 2; dy += 2) {
|
||||||
|
for (int dx = -2; dx <= 2; dx += 2) {
|
||||||
|
if (bitmapBads.get(col + dx, row + dy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int c = 0; c < colors; ++c) {
|
||||||
|
sum[c] += rawData[row + dy][(col + dx) * colors + c];
|
||||||
|
}
|
||||||
|
|
||||||
|
tot++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tot > 0) {
|
||||||
|
for (int c = 0; c < colors; ++c) {
|
||||||
|
rawData[row][col * colors + c] = sum[c] / tot;
|
||||||
|
}
|
||||||
|
|
||||||
|
counter ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter; // Number of interpolated pixels.
|
||||||
|
}
|
||||||
|
|
||||||
|
/* interpolateBadPixelsXtrans: correct raw pixels looking at the bitmap
|
||||||
|
* takes into consideration if there are multiple bad pixels in the neighborhood
|
||||||
|
*/
|
||||||
|
int RawImageSource::interpolateBadPixelsXtrans(const PixelsMap &bitmapBads)
|
||||||
|
{
|
||||||
|
constexpr float eps = 1.f;
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#pragma omp parallel for reduction(+:counter) schedule(dynamic,16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int row = 2; row < H - 2; ++row) {
|
||||||
|
for (int col = 2; col < W - 2; ++col) {
|
||||||
|
const int skip = bitmapBads.skipIfZero(col, row); //optimization for a stripe all zero
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
col += skip - 1; //-1 is because of col++ in cycle
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bitmapBads.get(col, row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float wtdsum = 0.f, norm = 0.f;
|
||||||
|
const unsigned int pixelColor = ri->XTRANSFC(row, col);
|
||||||
|
|
||||||
|
if (pixelColor == 1) {
|
||||||
|
// green channel. A green pixel can either be a solitary green pixel or a member of a 2x2 square of green pixels
|
||||||
|
if (ri->XTRANSFC(row, col - 1) == ri->XTRANSFC(row, col + 1)) {
|
||||||
|
// If left and right neighbor have same color, then this is a solitary green pixel
|
||||||
|
// For these the following pixels will be used for interpolation. Pixel to be interpolated is in center and marked with a P.
|
||||||
|
// Pairs of pixels used in this step are numbered. A pair will be used if none of the pixels of the pair is marked bad
|
||||||
|
// 0 means, the pixel has a different color and will not be used
|
||||||
|
// 0 1 0 2 0
|
||||||
|
// 3 5 0 6 4
|
||||||
|
// 0 0 P 0 0
|
||||||
|
// 4 6 0 5 3
|
||||||
|
// 0 2 0 1 0
|
||||||
|
for (int dx = -1; dx <= 1; dx += 2) { // pixels marked 5 or 6 in above example. Distance to P is sqrt(2) => weighting is 0.70710678f
|
||||||
|
if (bitmapBads.get(col + dx, row - 1) || bitmapBads.get(col - dx, row + 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float dirwt = 0.70710678f / (fabsf(rawData[row - 1][col + dx] - rawData[row + 1][col - dx]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row - 1][col + dx] + rawData[row + 1][col - dx]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int dx = -1; dx <= 1; dx += 2) { // pixels marked 1 or 2 on above example. Distance to P is sqrt(5) => weighting is 0.44721359f
|
||||||
|
if (bitmapBads.get(col + dx, row - 2) || bitmapBads.get(col - dx, row + 2)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float dirwt = 0.44721359f / (fabsf(rawData[row - 2][col + dx] - rawData[row + 2][col - dx]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row - 2][col + dx] + rawData[row + 2][col - dx]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int dx = -2; dx <= 2; dx += 4) { // pixels marked 3 or 4 on above example. Distance to P is sqrt(5) => weighting is 0.44721359f
|
||||||
|
if (bitmapBads.get(col + dx, row - 1) || bitmapBads.get(col - dx, row + 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float dirwt = 0.44721359f / (fabsf(rawData[row - 1][col + dx] - rawData[row + 1][col - dx]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row - 1][col + dx] + rawData[row + 1][col - dx]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this is a member of a 2x2 square of green pixels
|
||||||
|
// For these the following pixels will be used for interpolation. Pixel to be interpolated is at position P in the example.
|
||||||
|
// Pairs of pixels used in this step are numbered. A pair will be used if none of the pixels of the pair is marked bad
|
||||||
|
// 0 means, the pixel has a different color and will not be used
|
||||||
|
// 1 0 0 3
|
||||||
|
// 0 P 2 0
|
||||||
|
// 0 2 1 0
|
||||||
|
// 3 0 0 0
|
||||||
|
|
||||||
|
// pixels marked 1 in above example. Distance to P is sqrt(2) => weighting is 0.70710678f
|
||||||
|
const int offset1 = ri->XTRANSFC(row - 1, col - 1) == ri->XTRANSFC(row + 1, col + 1) ? 1 : -1;
|
||||||
|
|
||||||
|
if (!(bitmapBads.get(col - offset1, row - 1) || bitmapBads.get(col + offset1, row + 1))) {
|
||||||
|
const float dirwt = 0.70710678f / (fabsf(rawData[row - 1][col - offset1] - rawData[row + 1][col + offset1]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row - 1][col - offset1] + rawData[row + 1][col + offset1]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pixels marked 2 in above example. Distance to P is 1 => weighting is 1.f
|
||||||
|
int offsety = ri->XTRANSFC(row - 1, col) != 1 ? 1 : -1;
|
||||||
|
int offsetx = offset1 * offsety;
|
||||||
|
|
||||||
|
if (!(bitmapBads.get(col + offsetx, row) || bitmapBads.get(col, row + offsety))) {
|
||||||
|
const float dirwt = 1.f / (fabsf(rawData[row][col + offsetx] - rawData[row + offsety][col]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row][col + offsetx] + rawData[row + offsety][col]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int offsety2 = -offsety;
|
||||||
|
const int offsetx2 = -offsetx;
|
||||||
|
offsetx *= 2;
|
||||||
|
offsety *= 2;
|
||||||
|
|
||||||
|
// pixels marked 3 in above example. Distance to P is sqrt(5) => weighting is 0.44721359f
|
||||||
|
if (!(bitmapBads.get(col + offsetx, row + offsety2) || bitmapBads.get(col + offsetx2, row + offsety))) {
|
||||||
|
const float dirwt = 0.44721359f / (fabsf(rawData[row + offsety2][col + offsetx] - rawData[row + offsety][col + offsetx2]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row + offsety2][col + offsetx] + rawData[row + offsety][col + offsetx2]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// red and blue channel.
|
||||||
|
// Each red or blue pixel has exactly one neighbor of same color in distance 2 and four neighbors of same color which can be reached by a move of a knight in chess.
|
||||||
|
// For the distance 2 pixel (marked with an X) we generate a virtual counterpart (marked with a V)
|
||||||
|
// For red and blue channel following pixels will be used for interpolation. Pixel to be interpolated is in center and marked with a P.
|
||||||
|
// Pairs of pixels used in this step are numbered except for distance 2 pixels which are marked X and V. A pair will be used if none of the pixels of the pair is marked bad
|
||||||
|
// 0 1 0 0 0 0 0 X 0 0 remaining cases are symmetric
|
||||||
|
// 0 0 0 0 2 1 0 0 0 2
|
||||||
|
// X 0 P 0 V 0 0 P 0 0
|
||||||
|
// 0 0 0 0 1 0 0 0 0 0
|
||||||
|
// 0 2 0 0 0 0 2 V 1 0
|
||||||
|
|
||||||
|
// Find two knight moves landing on a pixel of same color as the pixel to be interpolated.
|
||||||
|
// If we look at first and last row of 5x5 square, we will find exactly two knight pixels.
|
||||||
|
// Additionally we know that the column of this pixel has 1 or -1 horizontal distance to the center pixel
|
||||||
|
// When we find a knight pixel, we get its counterpart, which has distance (+-3,+-3), where the signs of distance depend on the corner of the found knight pixel.
|
||||||
|
// These pixels are marked 1 or 2 in above examples. Distance to P is sqrt(5) => weighting is 0.44721359f
|
||||||
|
// The following loop simply scans the four possible places. To keep things simple, it does not stop after finding two knight pixels, because it will not find more than two
|
||||||
|
for (int d1 = -2, offsety = 3; d1 <= 2; d1 += 4, offsety -= 6) {
|
||||||
|
for (int d2 = -1, offsetx = 3; d2 < 1; d2 += 2, offsetx -= 6) {
|
||||||
|
if (ri->XTRANSFC(row + d1, col + d2) == pixelColor) {
|
||||||
|
if (!(bitmapBads.get(col + d2, row + d1) || bitmapBads.get(col + d2 + offsetx, row + d1 + offsety))) {
|
||||||
|
const float dirwt = 0.44721359f / (fabsf(rawData[row + d1][col + d2] - rawData[row + d1 + offsety][col + d2 + offsetx]) + eps);
|
||||||
|
wtdsum += dirwt * (rawData[row + d1][col + d2] + rawData[row + d1 + offsety][col + d2 + offsetx]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now scan for the pixel of same color in distance 2 in each direction (marked with an X in above examples).
|
||||||
|
bool distance2PixelFound = false;
|
||||||
|
int dx, dy;
|
||||||
|
|
||||||
|
// check horizontal
|
||||||
|
for (dx = -2, dy = 0; dx <= 2; dx += 4) {
|
||||||
|
if (ri->XTRANSFC(row, col + dx) == pixelColor) {
|
||||||
|
distance2PixelFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!distance2PixelFound) {
|
||||||
|
// no distance 2 pixel on horizontal, check vertical
|
||||||
|
for (dx = 0, dy = -2; dy <= 2; dy += 4) {
|
||||||
|
if (ri->XTRANSFC(row + dy, col) == pixelColor) {
|
||||||
|
distance2PixelFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the value of its virtual counterpart (marked with a V in above examples)
|
||||||
|
float virtualPixel;
|
||||||
|
|
||||||
|
if (dy == 0) {
|
||||||
|
virtualPixel = 0.5f * (rawData[row - 1][col - dx] + rawData[row + 1][col - dx]);
|
||||||
|
} else {
|
||||||
|
virtualPixel = 0.5f * (rawData[row - dy][col - 1] + rawData[row - dy][col + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and weight as usual. Distance to P is 2 => weighting is 0.5f
|
||||||
|
const float dirwt = 0.5f / (fabsf(virtualPixel - rawData[row + dy][col + dx]) + eps);
|
||||||
|
wtdsum += dirwt * (virtualPixel + rawData[row + dy][col + dx]);
|
||||||
|
norm += dirwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LIKELY(norm > 0.f)) { // This means, we found at least one pair of valid pixels in the steps above, likelihood of this case is about 99.999%
|
||||||
|
rawData[row][col] = wtdsum / (2.f * norm); //gradient weighted average, Factor of 2.f is an optimization to avoid multiplications in former steps
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter; // Number of interpolated pixels.
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search for hot or dead pixels in the image and update the map
|
||||||
|
* For each pixel compare its value to the average of similar color surrounding
|
||||||
|
* (Taken from Emil Martinec idea)
|
||||||
|
* (Optimized by Ingo Weyrich 2013 and 2015)
|
||||||
|
*/
|
||||||
|
int RawImageSource::findHotDeadPixels(PixelsMap &bpMap, const float thresh, const bool findHotPixels, const bool findDeadPixels) const
|
||||||
|
{
|
||||||
|
const float varthresh = (20.0 * (thresh / 100.0) + 1.0) / 24.f;
|
||||||
|
|
||||||
|
// allocate temporary buffer
|
||||||
|
float* cfablur = new float[H * W];
|
||||||
|
|
||||||
|
// counter for dead or hot pixels
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#pragma omp parallel
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#pragma omp for schedule(dynamic,16) nowait
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 2; i < H - 2; i++) {
|
||||||
|
for (int j = 2; j < W - 2; j++) {
|
||||||
|
const float temp = median(rawData[i - 2][j - 2], rawData[i - 2][j], rawData[i - 2][j + 2],
|
||||||
|
rawData[i][j - 2], rawData[i][j], rawData[i][j + 2],
|
||||||
|
rawData[i + 2][j - 2], rawData[i + 2][j], rawData[i + 2][j + 2]);
|
||||||
|
cfablur[i * W + j] = rawData[i][j] - temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process borders. Former version calculated the median using mirrored border which does not make sense because the original pixel loses weight
|
||||||
|
// Setting the difference between pixel and median for border pixels to zero should do the job not worse then former version
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#pragma omp single
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
for (int j = 0; j < W; ++j) {
|
||||||
|
cfablur[i * W + j] = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 2; i < H - 2; ++i) {
|
||||||
|
for (int j = 0; j < 2; ++j) {
|
||||||
|
cfablur[i * W + j] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = W - 2; j < W; ++j) {
|
||||||
|
cfablur[i * W + j] = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = H - 2; i < H; ++i) {
|
||||||
|
for (int j = 0; j < W; ++j) {
|
||||||
|
cfablur[i * W + j] = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#pragma omp barrier // barrier because of nowait clause above
|
||||||
|
|
||||||
|
#pragma omp for reduction(+:counter) schedule(dynamic,16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//cfa pixel heat/death evaluation
|
||||||
|
for (int rr = 2; rr < H - 2; ++rr) {
|
||||||
|
for (int cc = 2, rrmWpcc = rr * W + 2; cc < W - 2; ++cc, ++rrmWpcc) {
|
||||||
|
//evaluate pixel for heat/death
|
||||||
|
float pixdev = cfablur[rrmWpcc];
|
||||||
|
|
||||||
|
if (pixdev == 0.f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!findDeadPixels) && pixdev < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!findHotPixels) && pixdev > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixdev = fabsf(pixdev);
|
||||||
|
float hfnbrave = -pixdev;
|
||||||
|
|
||||||
|
#ifdef __SSE2__
|
||||||
|
// sum up 5*4 = 20 values using SSE
|
||||||
|
// 10 fabs function calls and 10 float additions with SSE
|
||||||
|
vfloat sum = vabsf(LVFU(cfablur[(rr - 2) * W + cc - 2])) + vabsf(LVFU(cfablur[(rr - 1) * W + cc - 2]));
|
||||||
|
sum += vabsf(LVFU(cfablur[(rr) * W + cc - 2]));
|
||||||
|
sum += vabsf(LVFU(cfablur[(rr + 1) * W + cc - 2]));
|
||||||
|
sum += vabsf(LVFU(cfablur[(rr + 2) * W + cc - 2]));
|
||||||
|
// horizontally add the values and add the result to hfnbrave
|
||||||
|
hfnbrave += vhadd(sum);
|
||||||
|
|
||||||
|
// add remaining 5 values of last column
|
||||||
|
for (int mm = rr - 2; mm <= rr + 2; ++mm) {
|
||||||
|
hfnbrave += fabsf(cfablur[mm * W + cc + 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// 25 fabs function calls and 25 float additions without SSE
|
||||||
|
for (int mm = rr - 2; mm <= rr + 2; ++mm) {
|
||||||
|
for (int nn = cc - 2; nn <= cc + 2; ++nn) {
|
||||||
|
hfnbrave += fabsf(cfablur[mm * W + nn]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (pixdev > varthresh * hfnbrave) {
|
||||||
|
// mark the pixel as "bad"
|
||||||
|
bpMap.set(cc, rr);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}//end of pixel evaluation
|
||||||
|
}
|
||||||
|
}//end of parallel processing
|
||||||
|
delete [] cfablur;
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RawImageSource::findZeroPixels(PixelsMap &bpMap) const
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#pragma omp parallel for reduction(+:counter) schedule(dynamic,16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < H; ++i) {
|
||||||
|
for (int j = 0; j < W; ++j) {
|
||||||
|
if (ri->data[i][j] == 0.f) {
|
||||||
|
bpMap.set(j, i);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -270,8 +270,11 @@ void dfInfo::updateBadPixelList( RawImage *df )
|
|||||||
|
|
||||||
// ************************* class DFManager *********************************
|
// ************************* class DFManager *********************************
|
||||||
|
|
||||||
void DFManager::init( Glib::ustring pathname )
|
void DFManager::init(const Glib::ustring& pathname)
|
||||||
{
|
{
|
||||||
|
if (pathname.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
std::vector<Glib::ustring> names;
|
std::vector<Glib::ustring> names;
|
||||||
|
|
||||||
auto dir = Gio::File::create_for_path (pathname);
|
auto dir = Gio::File::create_for_path (pathname);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <glibmm/ustring.h>
|
#include <glibmm/ustring.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include "pixelsmap.h"
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
|
|
||||||
namespace rtengine
|
namespace rtengine
|
||||||
@@ -77,7 +78,7 @@ protected:
|
|||||||
class DFManager
|
class DFManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void init( Glib::ustring pathname );
|
void init(const Glib::ustring &pathname);
|
||||||
Glib::ustring getPathname()
|
Glib::ustring getPathname()
|
||||||
{
|
{
|
||||||
return currentPath;
|
return currentPath;
|
||||||
|
|||||||
@@ -231,8 +231,11 @@ void ffInfo::updateRawImage()
|
|||||||
|
|
||||||
// ************************* class FFManager *********************************
|
// ************************* class FFManager *********************************
|
||||||
|
|
||||||
void FFManager::init( Glib::ustring pathname )
|
void FFManager::init(const Glib::ustring& pathname)
|
||||||
{
|
{
|
||||||
|
if (pathname.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
std::vector<Glib::ustring> names;
|
std::vector<Glib::ustring> names;
|
||||||
|
|
||||||
auto dir = Gio::File::create_for_path (pathname);
|
auto dir = Gio::File::create_for_path (pathname);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ protected:
|
|||||||
class FFManager
|
class FFManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void init( Glib::ustring pathname );
|
void init(const Glib::ustring &pathname);
|
||||||
Glib::ustring getPathname()
|
Glib::ustring getPathname()
|
||||||
{
|
{
|
||||||
return currentPath;
|
return currentPath;
|
||||||
@@ -86,7 +86,6 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::multimap<std::string, ffInfo> ffList_t;
|
typedef std::multimap<std::string, ffInfo> ffList_t;
|
||||||
typedef std::map<std::string, std::list<badPix> > bpList_t;
|
|
||||||
ffList_t ffList;
|
ffList_t ffList;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
Glib::ustring currentPath;
|
Glib::ustring currentPath;
|
||||||
|
|||||||
98
rtengine/pixelsmap.h
Normal file
98
rtengine/pixelsmap.h
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of RawTherapee.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2019 Gabor Horvath <hgabor@rawtherapee.com>
|
||||||
|
*
|
||||||
|
* RawTherapee is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* RawTherapee is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "noncopyable.h"
|
||||||
|
|
||||||
|
namespace rtengine
|
||||||
|
{
|
||||||
|
|
||||||
|
struct badPix {
|
||||||
|
uint16_t x;
|
||||||
|
uint16_t y;
|
||||||
|
badPix(uint16_t xc, uint16_t yc): x(xc), y(yc) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PixelsMap :
|
||||||
|
public NonCopyable
|
||||||
|
{
|
||||||
|
int w; // line width in base_t units
|
||||||
|
int h; // height
|
||||||
|
typedef unsigned long base_t;
|
||||||
|
static constexpr size_t base_t_size = sizeof(base_t);
|
||||||
|
base_t *pm;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PixelsMap(int width, int height)
|
||||||
|
: w((width / (base_t_size * 8)) + 1), h(height), pm(new base_t[h * w])
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
~PixelsMap()
|
||||||
|
{
|
||||||
|
delete [] pm;
|
||||||
|
}
|
||||||
|
int width() const
|
||||||
|
{
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
int height() const
|
||||||
|
{
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a pixel is set returns true
|
||||||
|
bool get(int x, int y) const
|
||||||
|
{
|
||||||
|
return (pm[y * w + x / (base_t_size * 8)] & (base_t)1 << (x % (base_t_size * 8))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set a pixel
|
||||||
|
void set(int x, int y)
|
||||||
|
{
|
||||||
|
pm[y * w + x / (base_t_size * 8)] |= (base_t)1 << (x % (base_t_size * 8)) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set pixels from a list
|
||||||
|
int set(const std::vector<badPix> &bp)
|
||||||
|
{
|
||||||
|
for (std::vector<badPix>::const_iterator iter = bp.begin(); iter != bp.end(); ++iter) {
|
||||||
|
set(iter->x, iter->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bp.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
memset(pm, 0, h * w * base_t_size);
|
||||||
|
}
|
||||||
|
// return 0 if at least one pixel in the word(base_t) is set, otherwise return the number of pixels to skip to the next word base_t
|
||||||
|
int skipIfZero(int x, int y) const
|
||||||
|
{
|
||||||
|
return pm[y * w + x / (base_t_size * 8)] == 0 ? base_t_size * 8 - x % (base_t_size * 8) : 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3215,23 +3215,23 @@ ProcParams::ProcParams()
|
|||||||
|
|
||||||
void ProcParams::setDefaults()
|
void ProcParams::setDefaults()
|
||||||
{
|
{
|
||||||
toneCurve = ToneCurveParams();
|
toneCurve = {};
|
||||||
|
|
||||||
labCurve = LCurveParams();
|
labCurve = {};
|
||||||
|
|
||||||
rgbCurves = RGBCurvesParams();
|
rgbCurves = {};
|
||||||
|
|
||||||
localContrast = LocalContrastParams();
|
localContrast = {};
|
||||||
|
|
||||||
colorToning = ColorToningParams();
|
colorToning = {};
|
||||||
|
|
||||||
sharpenEdge = SharpenEdgeParams();
|
sharpenEdge = {};
|
||||||
|
|
||||||
sharpenMicro = SharpenMicroParams();
|
sharpenMicro = {};
|
||||||
|
|
||||||
sharpening = SharpeningParams();
|
sharpening = {};
|
||||||
|
|
||||||
prsharpening = SharpeningParams();
|
prsharpening = {};
|
||||||
prsharpening.contrast = 15.0;
|
prsharpening.contrast = 15.0;
|
||||||
prsharpening.method = "rld";
|
prsharpening.method = "rld";
|
||||||
prsharpening.deconvamount = 100;
|
prsharpening.deconvamount = 100;
|
||||||
@@ -3239,71 +3239,72 @@ void ProcParams::setDefaults()
|
|||||||
prsharpening.deconviter = 100;
|
prsharpening.deconviter = 100;
|
||||||
prsharpening.deconvdamping = 0;
|
prsharpening.deconvdamping = 0;
|
||||||
|
|
||||||
vibrance = VibranceParams();
|
vibrance = {};
|
||||||
|
|
||||||
wb = WBParams();
|
wb = {};
|
||||||
|
|
||||||
colorappearance = ColorAppearanceParams();
|
colorappearance = {};
|
||||||
|
|
||||||
defringe = DefringeParams();
|
defringe = {};
|
||||||
|
|
||||||
impulseDenoise = ImpulseDenoiseParams();
|
impulseDenoise = {};
|
||||||
|
|
||||||
dirpyrDenoise = DirPyrDenoiseParams();
|
dirpyrDenoise = {};
|
||||||
|
|
||||||
epd = EPDParams();
|
epd = {};
|
||||||
|
|
||||||
fattal = FattalToneMappingParams();
|
fattal = {};
|
||||||
|
|
||||||
sh = SHParams();
|
sh = {};
|
||||||
|
|
||||||
crop = CropParams();
|
crop = {};
|
||||||
|
|
||||||
coarse = CoarseTransformParams();
|
coarse = {};
|
||||||
|
|
||||||
commonTrans = CommonTransformParams();
|
commonTrans = {};
|
||||||
|
|
||||||
rotate = RotateParams();
|
rotate = {};
|
||||||
|
|
||||||
distortion = DistortionParams();
|
distortion = {};
|
||||||
|
|
||||||
lensProf = LensProfParams();
|
lensProf = {};
|
||||||
|
|
||||||
perspective = PerspectiveParams();
|
perspective = {};
|
||||||
|
|
||||||
gradient = GradientParams();
|
gradient = {};
|
||||||
|
|
||||||
locallab = LocallabParams();
|
pcvignette = {};
|
||||||
|
|
||||||
pcvignette = PCVignetteParams();
|
locallab = {};
|
||||||
|
|
||||||
vignetting = VignettingParams();
|
|
||||||
|
|
||||||
chmixer = ChannelMixerParams();
|
vignetting = {};
|
||||||
|
|
||||||
blackwhite = BlackWhiteParams();
|
chmixer = {};
|
||||||
|
|
||||||
cacorrection = CACorrParams();
|
blackwhite = {};
|
||||||
|
|
||||||
resize = ResizeParams();
|
cacorrection = {};
|
||||||
|
|
||||||
icm = ColorManagementParams();
|
resize = {};
|
||||||
|
|
||||||
wavelet = WaveletParams();
|
icm = {};
|
||||||
|
|
||||||
dirpyrequalizer = DirPyrEqualizerParams();
|
wavelet = {};
|
||||||
|
|
||||||
hsvequalizer = HSVEqualizerParams();
|
dirpyrequalizer = {};
|
||||||
|
|
||||||
filmSimulation = FilmSimulationParams();
|
hsvequalizer = {};
|
||||||
|
|
||||||
softlight = SoftLightParams();
|
filmSimulation = {};
|
||||||
|
|
||||||
dehaze = DehazeParams();
|
softlight = {};
|
||||||
|
|
||||||
raw = RAWParams();
|
dehaze = {};
|
||||||
|
|
||||||
metadata = MetaDataParams();
|
raw = {};
|
||||||
|
|
||||||
|
metadata = {};
|
||||||
exif.clear();
|
exif.clear();
|
||||||
iptc.clear();
|
iptc.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -25,82 +25,10 @@
|
|||||||
|
|
||||||
#include "dcraw.h"
|
#include "dcraw.h"
|
||||||
#include "imageformat.h"
|
#include "imageformat.h"
|
||||||
#include "noncopyable.h"
|
|
||||||
|
|
||||||
namespace rtengine
|
namespace rtengine
|
||||||
{
|
{
|
||||||
|
|
||||||
struct badPix {
|
|
||||||
uint16_t x;
|
|
||||||
uint16_t y;
|
|
||||||
badPix(uint16_t xc, uint16_t yc): x(xc), y(yc) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class PixelsMap :
|
|
||||||
public NonCopyable
|
|
||||||
{
|
|
||||||
int w; // line width in base_t units
|
|
||||||
int h; // height
|
|
||||||
typedef unsigned long base_t;
|
|
||||||
static const size_t base_t_size = sizeof(base_t);
|
|
||||||
base_t *pm;
|
|
||||||
|
|
||||||
public:
|
|
||||||
PixelsMap(int width, int height)
|
|
||||||
: h(height)
|
|
||||||
{
|
|
||||||
w = (width / (base_t_size * 8)) + 1;
|
|
||||||
pm = new base_t [h * w ];
|
|
||||||
memset(pm, 0, h * w * base_t_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
~PixelsMap()
|
|
||||||
{
|
|
||||||
delete [] pm;
|
|
||||||
}
|
|
||||||
int width() const
|
|
||||||
{
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
int height() const
|
|
||||||
{
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if a pixel is set returns true
|
|
||||||
bool get(int x, int y)
|
|
||||||
{
|
|
||||||
return (pm[y * w + x / (base_t_size * 8) ] & (base_t)1 << (x % (base_t_size * 8))) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set a pixel
|
|
||||||
void set(int x, int y)
|
|
||||||
{
|
|
||||||
pm[y * w + x / (base_t_size * 8) ] |= (base_t)1 << (x % (base_t_size * 8)) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set pixels from a list
|
|
||||||
int set(std::vector<badPix> &bp)
|
|
||||||
{
|
|
||||||
for (std::vector<badPix>::iterator iter = bp.begin(); iter != bp.end(); ++iter) {
|
|
||||||
set(iter->x, iter->y);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bp.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
memset(pm, 0, h * w * base_t_size);
|
|
||||||
}
|
|
||||||
// return 0 if at least one pixel in the word(base_t) is set, otherwise return the number of pixels to skip to the next word base_t
|
|
||||||
int skipIfZero(int x, int y)
|
|
||||||
{
|
|
||||||
return pm[y * w + x / (base_t_size * 8) ] == 0 ? base_t_size * 8 - x % (base_t_size * 8) : 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class RawImage: public DCraw
|
class RawImage: public DCraw
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -932,542 +932,6 @@ void RawImageSource::convertColorSpace(Imagefloat* image, const ColorManagementP
|
|||||||
colorSpaceConversion(image, cmp, wb, pre_mul, embProfile, camProfile, imatrices.xyz_cam, (static_cast<const FramesData*>(getMetaData()))->getCamera());
|
colorSpaceConversion(image, cmp, wb, pre_mul, embProfile, camProfile, imatrices.xyz_cam, (static_cast<const FramesData*>(getMetaData()))->getCamera());
|
||||||
}
|
}
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
|
|
||||||
/* interpolateBadPixelsBayer: correct raw pixels looking at the bitmap
|
|
||||||
* takes into consideration if there are multiple bad pixels in the neighbourhood
|
|
||||||
*/
|
|
||||||
int RawImageSource::interpolateBadPixelsBayer(PixelsMap &bitmapBads, array2D<float> &rawData)
|
|
||||||
{
|
|
||||||
static const float eps = 1.f;
|
|
||||||
int counter = 0;
|
|
||||||
#ifdef _OPENMP
|
|
||||||
#pragma omp parallel for reduction(+:counter) schedule(dynamic,16)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int row = 2; row < H - 2; row++) {
|
|
||||||
for (int col = 2; col < W - 2; col++) {
|
|
||||||
int sk = bitmapBads.skipIfZero(col, row); //optimization for a stripe all zero
|
|
||||||
|
|
||||||
if (sk) {
|
|
||||||
col += sk - 1; //-1 is because of col++ in cycle
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bitmapBads.get(col, row)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float wtdsum = 0.f, norm = 0.f;
|
|
||||||
|
|
||||||
// diagonal interpolation
|
|
||||||
if (FC(row, col) == 1) {
|
|
||||||
// green channel. We can use closer pixels than for red or blue channel. Distance to centre pixel is sqrt(2) => weighting is 0.70710678
|
|
||||||
// For green channel following pixels will be used for interpolation. Pixel to be interpolated is in centre.
|
|
||||||
// 1 means that pixel is used in this step, if itself and his counterpart are not marked bad
|
|
||||||
// 0 0 0 0 0
|
|
||||||
// 0 1 0 1 0
|
|
||||||
// 0 0 0 0 0
|
|
||||||
// 0 1 0 1 0
|
|
||||||
// 0 0 0 0 0
|
|
||||||
for (int dx = -1; dx <= 1; dx += 2) {
|
|
||||||
if (bitmapBads.get(col + dx, row - 1) || bitmapBads.get(col - dx, row + 1)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float dirwt = 0.70710678f / (fabsf(rawData[row - 1][col + dx] - rawData[row + 1][col - dx]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row - 1][col + dx] + rawData[row + 1][col - dx]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// red and blue channel. Distance to centre pixel is sqrt(8) => weighting is 0.35355339
|
|
||||||
// For red and blue channel following pixels will be used for interpolation. Pixel to be interpolated is in centre.
|
|
||||||
// 1 means that pixel is used in this step, if itself and his counterpart are not marked bad
|
|
||||||
// 1 0 0 0 1
|
|
||||||
// 0 0 0 0 0
|
|
||||||
// 0 0 0 0 0
|
|
||||||
// 0 0 0 0 0
|
|
||||||
// 1 0 0 0 1
|
|
||||||
for (int dx = -2; dx <= 2; dx += 4) {
|
|
||||||
if (bitmapBads.get(col + dx, row - 2) || bitmapBads.get(col - dx, row + 2)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float dirwt = 0.35355339f / (fabsf(rawData[row - 2][col + dx] - rawData[row + 2][col - dx]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row - 2][col + dx] + rawData[row + 2][col - dx]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// channel independent. Distance to centre pixel is 2 => weighting is 0.5
|
|
||||||
// Additionally for all channel following pixels will be used for interpolation. Pixel to be interpolated is in centre.
|
|
||||||
// 1 means that pixel is used in this step, if itself and his counterpart are not marked bad
|
|
||||||
// 0 0 1 0 0
|
|
||||||
// 0 0 0 0 0
|
|
||||||
// 1 0 0 0 1
|
|
||||||
// 0 0 0 0 0
|
|
||||||
// 0 0 1 0 0
|
|
||||||
|
|
||||||
// horizontal interpolation
|
|
||||||
if (!(bitmapBads.get(col - 2, row) || bitmapBads.get(col + 2, row))) {
|
|
||||||
float dirwt = 0.5f / (fabsf(rawData[row][col - 2] - rawData[row][col + 2]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row][col - 2] + rawData[row][col + 2]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// vertical interpolation
|
|
||||||
if (!(bitmapBads.get(col, row - 2) || bitmapBads.get(col, row + 2))) {
|
|
||||||
float dirwt = 0.5f / (fabsf(rawData[row - 2][col] - rawData[row + 2][col]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row - 2][col] + rawData[row + 2][col]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LIKELY(norm > 0.f)) { // This means, we found at least one pair of valid pixels in the steps above, likelihood of this case is about 99.999%
|
|
||||||
rawData[row][col] = wtdsum / (2.f * norm); //gradient weighted average, Factor of 2.f is an optimization to avoid multiplications in former steps
|
|
||||||
counter++;
|
|
||||||
} else { //backup plan -- simple average. Same method for all channels. We could improve this, but it's really unlikely that this case happens
|
|
||||||
int tot = 0;
|
|
||||||
float sum = 0;
|
|
||||||
|
|
||||||
for (int dy = -2; dy <= 2; dy += 2) {
|
|
||||||
for (int dx = -2; dx <= 2; dx += 2) {
|
|
||||||
if (bitmapBads.get(col + dx, row + dy)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sum += rawData[row + dy][col + dx];
|
|
||||||
tot++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tot > 0) {
|
|
||||||
rawData[row][col] = sum / tot;
|
|
||||||
counter ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return counter; // Number of interpolated pixels.
|
|
||||||
}
|
|
||||||
|
|
||||||
/* interpolateBadPixels3Colours: correct raw pixels looking at the bitmap
|
|
||||||
* takes into consideration if there are multiple bad pixels in the neighbourhood
|
|
||||||
*/
|
|
||||||
int RawImageSource::interpolateBadPixelsNColours(PixelsMap &bitmapBads, const int colours)
|
|
||||||
{
|
|
||||||
static const float eps = 1.f;
|
|
||||||
int counter = 0;
|
|
||||||
#ifdef _OPENMP
|
|
||||||
#pragma omp parallel for reduction(+:counter) schedule(dynamic,16)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int row = 2; row < H - 2; row++) {
|
|
||||||
for (int col = 2; col < W - 2; col++) {
|
|
||||||
int sk = bitmapBads.skipIfZero(col, row); //optimization for a stripe all zero
|
|
||||||
|
|
||||||
if (sk) {
|
|
||||||
col += sk - 1; //-1 is because of col++ in cycle
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bitmapBads.get(col, row)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float wtdsum[colours], norm[colours];
|
|
||||||
|
|
||||||
for (int i = 0; i < colours; ++i) {
|
|
||||||
wtdsum[i] = norm[i] = 0.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// diagonal interpolation
|
|
||||||
for (int dx = -1; dx <= 1; dx += 2) {
|
|
||||||
if (bitmapBads.get(col + dx, row - 1) || bitmapBads.get(col - dx, row + 1)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int c = 0; c < colours; c++) {
|
|
||||||
float dirwt = 0.70710678f / (fabsf(rawData[row - 1][(col + dx) * colours + c] - rawData[row + 1][(col - dx) * colours + c]) + eps);
|
|
||||||
wtdsum[c] += dirwt * (rawData[row - 1][(col + dx) * colours + c] + rawData[row + 1][(col - dx) * colours + c]);
|
|
||||||
norm[c] += dirwt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// horizontal interpolation
|
|
||||||
if (!(bitmapBads.get(col - 1, row) || bitmapBads.get(col + 1, row))) {
|
|
||||||
for (int c = 0; c < colours; c++) {
|
|
||||||
float dirwt = 1.f / (fabsf(rawData[row][(col - 1) * colours + c] - rawData[row][(col + 1) * colours + c]) + eps);
|
|
||||||
wtdsum[c] += dirwt * (rawData[row][(col - 1) * colours + c] + rawData[row][(col + 1) * colours + c]);
|
|
||||||
norm[c] += dirwt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// vertical interpolation
|
|
||||||
if (!(bitmapBads.get(col, row - 1) || bitmapBads.get(col, row + 1))) {
|
|
||||||
for (int c = 0; c < colours; c++) {
|
|
||||||
float dirwt = 1.f / (fabsf(rawData[row - 1][col * colours + c] - rawData[row + 1][col * colours + c]) + eps);
|
|
||||||
wtdsum[c] += dirwt * (rawData[row - 1][col * colours + c] + rawData[row + 1][col * colours + c]);
|
|
||||||
norm[c] += dirwt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LIKELY(norm[0] > 0.f)) { // This means, we found at least one pair of valid pixels in the steps above, likelihood of this case is about 99.999%
|
|
||||||
for (int c = 0; c < colours; c++) {
|
|
||||||
rawData[row][col * colours + c] = wtdsum[c] / (2.f * norm[c]); //gradient weighted average, Factor of 2.f is an optimization to avoid multiplications in former steps
|
|
||||||
}
|
|
||||||
|
|
||||||
counter++;
|
|
||||||
} else { //backup plan -- simple average. Same method for all channels. We could improve this, but it's really unlikely that this case happens
|
|
||||||
int tot = 0;
|
|
||||||
float sum[colours];
|
|
||||||
|
|
||||||
for (int i = 0; i < colours; ++i) {
|
|
||||||
sum[i] = 0.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int dy = -2; dy <= 2; dy += 2) {
|
|
||||||
for (int dx = -2; dx <= 2; dx += 2) {
|
|
||||||
if (bitmapBads.get(col + dx, row + dy)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int c = 0; c < colours; c++) {
|
|
||||||
sum[c] += rawData[row + dy][(col + dx) * colours + c];
|
|
||||||
}
|
|
||||||
|
|
||||||
tot++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tot > 0) {
|
|
||||||
for (int c = 0; c < colours; c++) {
|
|
||||||
rawData[row][col * colours + c] = sum[c] / tot;
|
|
||||||
}
|
|
||||||
|
|
||||||
counter ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return counter; // Number of interpolated pixels.
|
|
||||||
}
|
|
||||||
/* interpolateBadPixelsXtrans: correct raw pixels looking at the bitmap
|
|
||||||
* takes into consideration if there are multiple bad pixels in the neighbourhood
|
|
||||||
*/
|
|
||||||
int RawImageSource::interpolateBadPixelsXtrans(PixelsMap &bitmapBads)
|
|
||||||
{
|
|
||||||
static const float eps = 1.f;
|
|
||||||
int counter = 0;
|
|
||||||
#ifdef _OPENMP
|
|
||||||
#pragma omp parallel for reduction(+:counter) schedule(dynamic,16)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int row = 2; row < H - 2; row++) {
|
|
||||||
for (int col = 2; col < W - 2; col++) {
|
|
||||||
int skip = bitmapBads.skipIfZero(col, row); //optimization for a stripe all zero
|
|
||||||
|
|
||||||
if (skip) {
|
|
||||||
col += skip - 1; //-1 is because of col++ in cycle
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bitmapBads.get(col, row)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float wtdsum = 0.f, norm = 0.f;
|
|
||||||
unsigned int pixelColor = ri->XTRANSFC(row, col);
|
|
||||||
|
|
||||||
if (pixelColor == 1) {
|
|
||||||
// green channel. A green pixel can either be a solitary green pixel or a member of a 2x2 square of green pixels
|
|
||||||
if (ri->XTRANSFC(row, col - 1) == ri->XTRANSFC(row, col + 1)) {
|
|
||||||
// If left and right neighbour have same colour, then this is a solitary green pixel
|
|
||||||
// For these the following pixels will be used for interpolation. Pixel to be interpolated is in centre and marked with a P.
|
|
||||||
// Pairs of pixels used in this step are numbered. A pair will be used if none of the pixels of the pair is marked bad
|
|
||||||
// 0 means, the pixel has a different colour and will not be used
|
|
||||||
// 0 1 0 2 0
|
|
||||||
// 3 5 0 6 4
|
|
||||||
// 0 0 P 0 0
|
|
||||||
// 4 6 0 5 3
|
|
||||||
// 0 2 0 1 0
|
|
||||||
for (int dx = -1; dx <= 1; dx += 2) { // pixels marked 5 or 6 in above example. Distance to P is sqrt(2) => weighting is 0.70710678f
|
|
||||||
if (bitmapBads.get(col + dx, row - 1) || bitmapBads.get(col - dx, row + 1)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float dirwt = 0.70710678f / (fabsf(rawData[row - 1][col + dx] - rawData[row + 1][col - dx]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row - 1][col + dx] + rawData[row + 1][col - dx]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int dx = -1; dx <= 1; dx += 2) { // pixels marked 1 or 2 on above example. Distance to P is sqrt(5) => weighting is 0.44721359f
|
|
||||||
if (bitmapBads.get(col + dx, row - 2) || bitmapBads.get(col - dx, row + 2)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float dirwt = 0.44721359f / (fabsf(rawData[row - 2][col + dx] - rawData[row + 2][col - dx]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row - 2][col + dx] + rawData[row + 2][col - dx]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int dx = -2; dx <= 2; dx += 4) { // pixels marked 3 or 4 on above example. Distance to P is sqrt(5) => weighting is 0.44721359f
|
|
||||||
if (bitmapBads.get(col + dx, row - 1) || bitmapBads.get(col - dx, row + 1)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float dirwt = 0.44721359f / (fabsf(rawData[row - 1][col + dx] - rawData[row + 1][col - dx]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row - 1][col + dx] + rawData[row + 1][col - dx]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// this is a member of a 2x2 square of green pixels
|
|
||||||
// For these the following pixels will be used for interpolation. Pixel to be interpolated is at position P in the example.
|
|
||||||
// Pairs of pixels used in this step are numbered. A pair will be used if none of the pixels of the pair is marked bad
|
|
||||||
// 0 means, the pixel has a different colour and will not be used
|
|
||||||
// 1 0 0 3
|
|
||||||
// 0 P 2 0
|
|
||||||
// 0 2 1 0
|
|
||||||
// 3 0 0 0
|
|
||||||
|
|
||||||
// pixels marked 1 in above example. Distance to P is sqrt(2) => weighting is 0.70710678f
|
|
||||||
int offset1 = ri->XTRANSFC(row - 1, col - 1) == ri->XTRANSFC(row + 1, col + 1) ? 1 : -1;
|
|
||||||
|
|
||||||
if (!(bitmapBads.get(col - offset1, row - 1) || bitmapBads.get(col + offset1, row + 1))) {
|
|
||||||
float dirwt = 0.70710678f / (fabsf(rawData[row - 1][col - offset1] - rawData[row + 1][col + offset1]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row - 1][col - offset1] + rawData[row + 1][col + offset1]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pixels marked 2 in above example. Distance to P is 1 => weighting is 1.f
|
|
||||||
int offsety = (ri->XTRANSFC(row - 1, col) != 1 ? 1 : -1);
|
|
||||||
int offsetx = offset1 * offsety;
|
|
||||||
|
|
||||||
if (!(bitmapBads.get(col + offsetx, row) || bitmapBads.get(col, row + offsety))) {
|
|
||||||
float dirwt = 1.f / (fabsf(rawData[row][col + offsetx] - rawData[row + offsety][col]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row][col + offsetx] + rawData[row + offsety][col]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
|
|
||||||
int offsety2 = -offsety;
|
|
||||||
int offsetx2 = -offsetx;
|
|
||||||
offsetx *= 2;
|
|
||||||
offsety *= 2;
|
|
||||||
|
|
||||||
// pixels marked 3 in above example. Distance to P is sqrt(5) => weighting is 0.44721359f
|
|
||||||
if (!(bitmapBads.get(col + offsetx, row + offsety2) || bitmapBads.get(col + offsetx2, row + offsety))) {
|
|
||||||
float dirwt = 0.44721359f / (fabsf(rawData[row + offsety2][col + offsetx] - rawData[row + offsety][col + offsetx2]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row + offsety2][col + offsetx] + rawData[row + offsety][col + offsetx2]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// red and blue channel.
|
|
||||||
// Each red or blue pixel has exactly one neighbour of same colour in distance 2 and four neighbours of same colour which can be reached by a move of a knight in chess.
|
|
||||||
// For the distance 2 pixel (marked with an X) we generate a virtual counterpart (marked with a V)
|
|
||||||
// For red and blue channel following pixels will be used for interpolation. Pixel to be interpolated is in centre and marked with a P.
|
|
||||||
// Pairs of pixels used in this step are numbered except for distance 2 pixels which are marked X and V. A pair will be used if none of the pixels of the pair is marked bad
|
|
||||||
// 0 1 0 0 0 0 0 X 0 0 remaining cases are symmetric
|
|
||||||
// 0 0 0 0 2 1 0 0 0 2
|
|
||||||
// X 0 P 0 V 0 0 P 0 0
|
|
||||||
// 0 0 0 0 1 0 0 0 0 0
|
|
||||||
// 0 2 0 0 0 0 2 V 1 0
|
|
||||||
|
|
||||||
// Find two knight moves landing on a pixel of same colour as the pixel to be interpolated.
|
|
||||||
// If we look at first and last row of 5x5 square, we will find exactly two knight pixels.
|
|
||||||
// Additionally we know that the column of this pixel has 1 or -1 horizontal distance to the centre pixel
|
|
||||||
// When we find a knight pixel, we get its counterpart, which has distance (+-3,+-3), where the signs of distance depend on the corner of the found knight pixel.
|
|
||||||
// These pixels are marked 1 or 2 in above examples. Distance to P is sqrt(5) => weighting is 0.44721359f
|
|
||||||
// The following loop simply scans the four possible places. To keep things simple, it does not stop after finding two knight pixels, because it will not find more than two
|
|
||||||
for (int d1 = -2, offsety = 3; d1 <= 2; d1 += 4, offsety -= 6) {
|
|
||||||
for (int d2 = -1, offsetx = 3; d2 < 1; d2 += 2, offsetx -= 6) {
|
|
||||||
if (ri->XTRANSFC(row + d1, col + d2) == pixelColor) {
|
|
||||||
if (!(bitmapBads.get(col + d2, row + d1) || bitmapBads.get(col + d2 + offsetx, row + d1 + offsety))) {
|
|
||||||
float dirwt = 0.44721359f / (fabsf(rawData[row + d1][col + d2] - rawData[row + d1 + offsety][col + d2 + offsetx]) + eps);
|
|
||||||
wtdsum += dirwt * (rawData[row + d1][col + d2] + rawData[row + d1 + offsety][col + d2 + offsetx]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now scan for the pixel of same colour in distance 2 in each direction (marked with an X in above examples).
|
|
||||||
bool distance2PixelFound = false;
|
|
||||||
int dx, dy;
|
|
||||||
|
|
||||||
// check horizontal
|
|
||||||
for (dx = -2, dy = 0; dx <= 2 && !distance2PixelFound; dx += 4)
|
|
||||||
if (ri->XTRANSFC(row, col + dx) == pixelColor) {
|
|
||||||
distance2PixelFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!distance2PixelFound)
|
|
||||||
|
|
||||||
// no distance 2 pixel on horizontal, check vertical
|
|
||||||
for (dx = 0, dy = -2; dy <= 2 && !distance2PixelFound; dy += 4)
|
|
||||||
if (ri->XTRANSFC(row + dy, col) == pixelColor) {
|
|
||||||
distance2PixelFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate the value of its virtual counterpart (marked with a V in above examples)
|
|
||||||
float virtualPixel;
|
|
||||||
|
|
||||||
if (dy == 0) {
|
|
||||||
virtualPixel = 0.5f * (rawData[row - 1][col - dx] + rawData[row + 1][col - dx]);
|
|
||||||
} else {
|
|
||||||
virtualPixel = 0.5f * (rawData[row - dy][col - 1] + rawData[row - dy][col + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// and weight as usual. Distance to P is 2 => weighting is 0.5f
|
|
||||||
float dirwt = 0.5f / (fabsf(virtualPixel - rawData[row + dy][col + dx]) + eps);
|
|
||||||
wtdsum += dirwt * (virtualPixel + rawData[row + dy][col + dx]);
|
|
||||||
norm += dirwt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LIKELY(norm > 0.f)) { // This means, we found at least one pair of valid pixels in the steps above, likelihood of this case is about 99.999%
|
|
||||||
rawData[row][col] = wtdsum / (2.f * norm); //gradient weighted average, Factor of 2.f is an optimization to avoid multiplications in former steps
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return counter; // Number of interpolated pixels.
|
|
||||||
}
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
|
|
||||||
/* Search for hot or dead pixels in the image and update the map
|
|
||||||
* For each pixel compare its value to the average of similar colour surrounding
|
|
||||||
* (Taken from Emil Martinec idea)
|
|
||||||
* (Optimized by Ingo Weyrich 2013 and 2015)
|
|
||||||
*/
|
|
||||||
int RawImageSource::findHotDeadPixels( PixelsMap &bpMap, float thresh, bool findHotPixels, bool findDeadPixels )
|
|
||||||
{
|
|
||||||
float varthresh = (20.0 * (thresh / 100.0) + 1.0) / 24.f;
|
|
||||||
|
|
||||||
// allocate temporary buffer
|
|
||||||
float* cfablur;
|
|
||||||
cfablur = (float (*)) malloc(H * W * sizeof * cfablur);
|
|
||||||
|
|
||||||
// counter for dead or hot pixels
|
|
||||||
int counter = 0;
|
|
||||||
|
|
||||||
#ifdef _OPENMP
|
|
||||||
#pragma omp parallel
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#ifdef _OPENMP
|
|
||||||
#pragma omp for schedule(dynamic,16) nowait
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 2; i < H - 2; i++) {
|
|
||||||
for (int j = 2; j < W - 2; j++) {
|
|
||||||
const float& temp = median(rawData[i - 2][j - 2], rawData[i - 2][j], rawData[i - 2][j + 2],
|
|
||||||
rawData[i][j - 2], rawData[i][j], rawData[i][j + 2],
|
|
||||||
rawData[i + 2][j - 2], rawData[i + 2][j], rawData[i + 2][j + 2]);
|
|
||||||
cfablur[i * W + j] = rawData[i][j] - temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// process borders. Former version calculated the median using mirrored border which does not make sense because the original pixel loses weight
|
|
||||||
// Setting the difference between pixel and median for border pixels to zero should do the job not worse then former version
|
|
||||||
#ifdef _OPENMP
|
|
||||||
#pragma omp single
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
for (int j = 0; j < W; j++) {
|
|
||||||
cfablur[i * W + j] = 0.f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 2; i < H - 2; i++) {
|
|
||||||
for (int j = 0; j < 2; j++) {
|
|
||||||
cfablur[i * W + j] = 0.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = W - 2; j < W; j++) {
|
|
||||||
cfablur[i * W + j] = 0.f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = H - 2; i < H; i++) {
|
|
||||||
for (int j = 0; j < W; j++) {
|
|
||||||
cfablur[i * W + j] = 0.f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef _OPENMP
|
|
||||||
#pragma omp barrier // barrier because of nowait clause above
|
|
||||||
|
|
||||||
#pragma omp for reduction(+:counter) schedule(dynamic,16)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//cfa pixel heat/death evaluation
|
|
||||||
for (int rr = 2; rr < H - 2; rr++) {
|
|
||||||
int rrmWpcc = rr * W + 2;
|
|
||||||
|
|
||||||
for (int cc = 2; cc < W - 2; cc++, rrmWpcc++) {
|
|
||||||
//evaluate pixel for heat/death
|
|
||||||
float pixdev = cfablur[rrmWpcc];
|
|
||||||
|
|
||||||
if (pixdev == 0.f) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!findDeadPixels) && pixdev < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!findHotPixels) && pixdev > 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pixdev = fabsf(pixdev);
|
|
||||||
float hfnbrave = -pixdev;
|
|
||||||
|
|
||||||
#ifdef __SSE2__
|
|
||||||
// sum up 5*4 = 20 values using SSE
|
|
||||||
// 10 fabs function calls and float 10 additions with SSE
|
|
||||||
vfloat sum = vabsf(LVFU(cfablur[(rr - 2) * W + cc - 2])) + vabsf(LVFU(cfablur[(rr - 1) * W + cc - 2]));
|
|
||||||
sum += vabsf(LVFU(cfablur[(rr) * W + cc - 2]));
|
|
||||||
sum += vabsf(LVFU(cfablur[(rr + 1) * W + cc - 2]));
|
|
||||||
sum += vabsf(LVFU(cfablur[(rr + 2) * W + cc - 2]));
|
|
||||||
// horizontally add the values and add the result to hfnbrave
|
|
||||||
hfnbrave += vhadd(sum);
|
|
||||||
|
|
||||||
// add remaining 5 values of last column
|
|
||||||
for (int mm = rr - 2; mm <= rr + 2; mm++) {
|
|
||||||
hfnbrave += fabsf(cfablur[mm * W + cc + 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// 25 fabs function calls and 25 float additions without SSE
|
|
||||||
for (int mm = rr - 2; mm <= rr + 2; mm++) {
|
|
||||||
for (int nn = cc - 2; nn <= cc + 2; nn++) {
|
|
||||||
hfnbrave += fabsf(cfablur[mm * W + nn]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (pixdev > varthresh * hfnbrave) {
|
|
||||||
// mark the pixel as "bad"
|
|
||||||
bpMap.set(cc, rr);
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}//end of pixel evaluation
|
|
||||||
}
|
|
||||||
}//end of parallel processing
|
|
||||||
free(cfablur);
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
|
|
||||||
void RawImageSource::getFullSize(int& w, int& h, int tr)
|
void RawImageSource::getFullSize(int& w, int& h, int tr)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -1747,23 +1211,13 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens
|
|||||||
printf("Subtracting Darkframe:%s\n", rid->get_filename().c_str());
|
printf("Subtracting Darkframe:%s\n", rid->get_filename().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelsMap *bitmapBads = nullptr;
|
std::unique_ptr<PixelsMap> bitmapBads;
|
||||||
|
|
||||||
int totBP = 0; // Hold count of bad pixels to correct
|
int totBP = 0; // Hold count of bad pixels to correct
|
||||||
|
|
||||||
if (ri->zeroIsBad()) { // mark all pixels with value zero as bad, has to be called before FF and DF. dcraw sets this flag only for some cameras (mainly Panasonic and Leica)
|
if (ri->zeroIsBad()) { // mark all pixels with value zero as bad, has to be called before FF and DF. dcraw sets this flag only for some cameras (mainly Panasonic and Leica)
|
||||||
bitmapBads = new PixelsMap(W, H);
|
bitmapBads.reset(new PixelsMap(W, H));
|
||||||
#ifdef _OPENMP
|
totBP = findZeroPixels(*(bitmapBads.get()));
|
||||||
#pragma omp parallel for reduction(+:totBP) schedule(dynamic,16)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 0; i < H; i++)
|
|
||||||
for (int j = 0; j < W; j++) {
|
|
||||||
if (ri->data[i][j] == 0.f) {
|
|
||||||
bitmapBads->set(j, i);
|
|
||||||
totBP++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings->verbose) {
|
if (settings->verbose) {
|
||||||
printf("%d pixels with value zero marked as bad pixels\n", totBP);
|
printf("%d pixels with value zero marked as bad pixels\n", totBP);
|
||||||
@@ -1830,7 +1284,7 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens
|
|||||||
|
|
||||||
if (bp) {
|
if (bp) {
|
||||||
if (!bitmapBads) {
|
if (!bitmapBads) {
|
||||||
bitmapBads = new PixelsMap(W, H);
|
bitmapBads.reset(new PixelsMap(W, H));
|
||||||
}
|
}
|
||||||
|
|
||||||
totBP += bitmapBads->set(*bp);
|
totBP += bitmapBads->set(*bp);
|
||||||
@@ -1851,7 +1305,7 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens
|
|||||||
|
|
||||||
if (bp) {
|
if (bp) {
|
||||||
if (!bitmapBads) {
|
if (!bitmapBads) {
|
||||||
bitmapBads = new PixelsMap(W, H);
|
bitmapBads.reset(new PixelsMap(W, H));
|
||||||
}
|
}
|
||||||
|
|
||||||
totBP += bitmapBads->set(*bp);
|
totBP += bitmapBads->set(*bp);
|
||||||
@@ -1928,10 +1382,10 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!bitmapBads) {
|
if (!bitmapBads) {
|
||||||
bitmapBads = new PixelsMap(W, H);
|
bitmapBads.reset(new PixelsMap(W, H));
|
||||||
}
|
}
|
||||||
|
|
||||||
int nFound = findHotDeadPixels(*bitmapBads, raw.hotdeadpix_thresh, raw.hotPixelFilter, raw.deadPixelFilter);
|
int nFound = findHotDeadPixels(*(bitmapBads.get()), raw.hotdeadpix_thresh, raw.hotPixelFilter, raw.deadPixelFilter );
|
||||||
totBP += nFound;
|
totBP += nFound;
|
||||||
|
|
||||||
if (settings->verbose && nFound > 0) {
|
if (settings->verbose && nFound > 0) {
|
||||||
@@ -1943,10 +1397,10 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens
|
|||||||
PDAFLinesFilter f(ri);
|
PDAFLinesFilter f(ri);
|
||||||
|
|
||||||
if (!bitmapBads) {
|
if (!bitmapBads) {
|
||||||
bitmapBads = new PixelsMap(W, H);
|
bitmapBads.reset(new PixelsMap(W, H));
|
||||||
}
|
}
|
||||||
|
|
||||||
int n = f.mark(rawData, *bitmapBads);
|
int n = f.mark(rawData, *(bitmapBads.get()));
|
||||||
totBP += n;
|
totBP += n;
|
||||||
|
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
@@ -2010,15 +1464,15 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens
|
|||||||
if (ri->getSensorType() == ST_BAYER) {
|
if (ri->getSensorType() == ST_BAYER) {
|
||||||
if (numFrames == 4) {
|
if (numFrames == 4) {
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
interpolateBadPixelsBayer(*bitmapBads, *rawDataFrames[i]);
|
interpolateBadPixelsBayer(*(bitmapBads.get()), *rawDataFrames[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
interpolateBadPixelsBayer(*bitmapBads, rawData);
|
interpolateBadPixelsBayer(*(bitmapBads.get()), rawData);
|
||||||
}
|
}
|
||||||
} else if (ri->getSensorType() == ST_FUJI_XTRANS) {
|
} else if (ri->getSensorType() == ST_FUJI_XTRANS) {
|
||||||
interpolateBadPixelsXtrans(*bitmapBads);
|
interpolateBadPixelsXtrans(*(bitmapBads.get()));
|
||||||
} else {
|
} else {
|
||||||
interpolateBadPixelsNColours(*bitmapBads, ri->get_colors());
|
interpolateBadPixelsNColours(*(bitmapBads.get()), ri->get_colors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2072,10 +1526,6 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens
|
|||||||
printf("Preprocessing: %d usec\n", t2.etime(t1));
|
printf("Preprocessing: %d usec\n", t2.etime(t1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmapBads) {
|
|
||||||
delete bitmapBads;
|
|
||||||
}
|
|
||||||
|
|
||||||
rawDirty = true;
|
rawDirty = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "dcp.h"
|
#include "dcp.h"
|
||||||
#include "iimage.h"
|
#include "iimage.h"
|
||||||
#include "imagesource.h"
|
#include "imagesource.h"
|
||||||
|
#include "pixelsmap.h"
|
||||||
|
|
||||||
#define HR_SCALE 2
|
#define HR_SCALE 2
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ protected:
|
|||||||
void transformRect(const PreviewProps &pp, int tran, int &sx1, int &sy1, int &width, int &height, int &fw);
|
void transformRect(const PreviewProps &pp, int tran, int &sx1, int &sy1, int &width, int &height, int &fw);
|
||||||
void transformPosition(int x, int y, int tran, int& tx, int& ty);
|
void transformPosition(int x, int y, int tran, int& tx, int& ty);
|
||||||
|
|
||||||
unsigned FC(int row, int col)
|
unsigned FC(int row, int col) const
|
||||||
{
|
{
|
||||||
return ri->FC(row, col);
|
return ri->FC(row, col);
|
||||||
}
|
}
|
||||||
@@ -258,11 +259,11 @@ protected:
|
|||||||
);
|
);
|
||||||
void ddct8x8s(int isgn, float a[8][8]);
|
void ddct8x8s(int isgn, float a[8][8]);
|
||||||
|
|
||||||
int interpolateBadPixelsBayer(PixelsMap &bitmapBads, array2D<float> &rawData);
|
int interpolateBadPixelsBayer(const PixelsMap &bitmapBads, array2D<float> &rawData);
|
||||||
int interpolateBadPixelsNColours(PixelsMap &bitmapBads, const int colours);
|
int interpolateBadPixelsNColours(const PixelsMap &bitmapBads, int colours);
|
||||||
int interpolateBadPixelsXtrans(PixelsMap &bitmapBads);
|
int interpolateBadPixelsXtrans(const PixelsMap &bitmapBads);
|
||||||
int findHotDeadPixels(PixelsMap &bpMap, float thresh, bool findHotPixels, bool findDeadPixels);
|
int findHotDeadPixels(PixelsMap &bpMap, float thresh, bool findHotPixels, bool findDeadPixels) const;
|
||||||
|
int findZeroPixels(PixelsMap &bpMap) const;
|
||||||
void cfa_linedn (float linenoiselevel, bool horizontal, bool vertical, const CFALineDenoiseRowBlender &rowblender);//Emil's line denoise
|
void cfa_linedn (float linenoiselevel, bool horizontal, bool vertical, const CFALineDenoiseRowBlender &rowblender);//Emil's line denoise
|
||||||
|
|
||||||
void green_equilibrate_global(array2D<float> &rawData);
|
void green_equilibrate_global(array2D<float> &rawData);
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ ClutComboBox::ClutModel::ClutModel(const Glib::ustring &path)
|
|||||||
{
|
{
|
||||||
m_model = Gtk::TreeStore::create (m_columns);
|
m_model = Gtk::TreeStore::create (m_columns);
|
||||||
//set_model (m_model);
|
//set_model (m_model);
|
||||||
count = parseDir(path);
|
count = path.empty() ? 0 : parseDir(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClutComboBox::ClutModel::parseDir(const Glib::ustring& path)
|
int ClutComboBox::ClutModel::parseDir(const Glib::ustring& path)
|
||||||
|
|||||||
10
tools/osx/Info.plist-bin.in
Normal file
10
tools/osx/Info.plist-bin.in
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>RawTherapee-bin</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.rawtherapee.rawtherapee</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/sh
|
||||||
|
|
||||||
cd "$(dirname "$0")" || exit 1
|
cd "$(dirname "$0")" || exit 1
|
||||||
|
|
||||||
@@ -7,21 +7,8 @@ app="${cwd%/Contents/*}"
|
|||||||
lib="${app}/Contents/Frameworks"
|
lib="${app}/Contents/Frameworks"
|
||||||
resources="${app}/Contents/Resources"
|
resources="${app}/Contents/Resources"
|
||||||
etc="${resources}"/etc
|
etc="${resources}"/etc
|
||||||
|
|
||||||
### Pending deletion:
|
|
||||||
# See https://github.com/Beep6581/RawTherapee/issues/1779
|
|
||||||
# cups_dir=/tmp/RT5
|
|
||||||
# install -d "${cups_dir}"
|
|
||||||
# cp -f /usr/lib/libcups.2.dylib "${cups_dir}"
|
|
||||||
# export DYLD_LIBRARY_PATH="${lib}:${cups_dir}"
|
|
||||||
|
|
||||||
# export GTK_EXE_PREFIX="${resources}"
|
|
||||||
# export GTK_DATA_PREFIX="${resources}"
|
|
||||||
export XDG_DATA_DIRS="${resources}/share"
|
export XDG_DATA_DIRS="${resources}/share"
|
||||||
# export GTK_IM_MODULE_FILE="${etc}/gtk-3.0/gtk.immodules"
|
|
||||||
|
|
||||||
export DYLD_LIBRARY_PATH="${lib}"
|
export DYLD_LIBRARY_PATH="${lib}"
|
||||||
|
|
||||||
export GTK_PATH="${lib}/gtk-3.0/3.0.0"
|
export GTK_PATH="${lib}/gtk-3.0/3.0.0"
|
||||||
export XDG_DATA_HOME="${resources}/share"
|
export XDG_DATA_HOME="${resources}/share"
|
||||||
export GSETTINGS_SCHEMA_DIR="${resources}/share/glib-2.0/schemas"
|
export GSETTINGS_SCHEMA_DIR="${resources}/share/glib-2.0/schemas"
|
||||||
@@ -31,25 +18,13 @@ export GDK_PIXBUF_MODULEDIR="${lib}/gdk-pixbuf-2.0/2.10.0/loaders"
|
|||||||
export RT_SETTINGS="${HOME}/Library/Application Support/RawTherapee/config"
|
export RT_SETTINGS="${HOME}/Library/Application Support/RawTherapee/config"
|
||||||
export RT_CACHE="${HOME}/Library/Application Support/RawTherapee/cache"
|
export RT_CACHE="${HOME}/Library/Application Support/RawTherapee/cache"
|
||||||
|
|
||||||
### Pending deletion:
|
|
||||||
# Environment variables for X11 backend
|
|
||||||
#if [[ -d ${etc}/fonts ]]; then
|
|
||||||
# export FONTCONFIG_PATH="${etc}/fonts"
|
|
||||||
#fi
|
|
||||||
|
|
||||||
# Strip out system argument
|
# Strip out system argument
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-psn_*) shift ;;
|
-psn_*) shift ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Commented-out as part of "crash-on-startup part 2" fix, see https://github.com/Beep6581/RawTherapee/issues/3882#issuecomment-311703141
|
|
||||||
#if [[ -d "/tmp/RawTherapee.app" ]]; then
|
|
||||||
# rm -rf "/tmp/RawTherapee.app"
|
|
||||||
#fi
|
|
||||||
#ln -sf "${app}" /tmp
|
|
||||||
|
|
||||||
# Prevent crash when directory name contains special characters
|
# Prevent crash when directory name contains special characters
|
||||||
AppleLocale=`defaults read -g AppleLocale`
|
AppleLocale=`defaults read -g AppleLocale`
|
||||||
export LANG=${AppleLocale%@*}.UTF-8
|
export LANG=${AppleLocale%@*}.UTF-8
|
||||||
|
|
||||||
exec "${cwd}/rawtherapee-bin" "$@"
|
exec "${cwd}/bin/rawtherapee-bin" "$@"
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ ETC="${RESOURCES}/etc"
|
|||||||
EXECUTABLE="${MACOS}/rawtherapee"
|
EXECUTABLE="${MACOS}/rawtherapee"
|
||||||
|
|
||||||
msg "Removing old files:"
|
msg "Removing old files:"
|
||||||
rm -rf "${APP}" "${PROJECT_NAME}_*.dmg"
|
rm -rf "${APP}" "${PROJECT_NAME}_*.dmg" "*zip"
|
||||||
|
|
||||||
msg "Creating bundle container:"
|
msg "Creating bundle container:"
|
||||||
install -d "${RESOURCES}" \
|
install -d "${RESOURCES}" \
|
||||||
@@ -134,7 +134,7 @@ rm -r "${LIB}"/gdk-pixbuf-2.0
|
|||||||
|
|
||||||
"${GTK_PREFIX}/bin/gdk-pixbuf-query-loaders" "${LIB}"/libpix*.so > "${ETC}/gtk-3.0/gdk-pixbuf.loaders"
|
"${GTK_PREFIX}/bin/gdk-pixbuf-query-loaders" "${LIB}"/libpix*.so > "${ETC}/gtk-3.0/gdk-pixbuf.loaders"
|
||||||
"${GTK_PREFIX}/bin/gtk-query-immodules-3.0" "${LIB}"/{im*.so,libprint*.so} > "${ETC}/gtk-3.0/gtk.immodules"
|
"${GTK_PREFIX}/bin/gtk-query-immodules-3.0" "${LIB}"/{im*.so,libprint*.so} > "${ETC}/gtk-3.0/gtk.immodules"
|
||||||
sed -i "" -e "s|${PWD}/RawTherapee.app/Contents/|@executable_path/../|" "${ETC}/gtk-3.0/gdk-pixbuf.loaders" "${ETC}/gtk-3.0/gtk.immodules"
|
sed -i "" -e "s|${PWD}/RawTherapee.app/Contents/|/Applications/RawTherapee.app/Contents/|" "${ETC}/gtk-3.0/gdk-pixbuf.loaders" "${ETC}/gtk-3.0/gtk.immodules"
|
||||||
|
|
||||||
ditto {"${GTK_PREFIX}","${RESOURCES}"}/share/glib-2.0/schemas
|
ditto {"${GTK_PREFIX}","${RESOURCES}"}/share/glib-2.0/schemas
|
||||||
"${GTK_PREFIX}/bin/glib-compile-schemas" "${RESOURCES}/share/glib-2.0/schemas"
|
"${GTK_PREFIX}/bin/glib-compile-schemas" "${RESOURCES}/share/glib-2.0/schemas"
|
||||||
@@ -155,6 +155,12 @@ ditto {"${GTK_PREFIX}","${RESOURCES}"}/share/icons/Adwaita/index.theme
|
|||||||
# Copy libjpeg-turbo into the app bundle
|
# Copy libjpeg-turbo into the app bundle
|
||||||
cp /opt/local/lib/libjpeg.62.dylib "${RESOURCES}/../Frameworks"
|
cp /opt/local/lib/libjpeg.62.dylib "${RESOURCES}/../Frameworks"
|
||||||
|
|
||||||
|
# Copy libexpat into the app bundle
|
||||||
|
cp /opt/local/lib/libexpat.1.dylib "${RESOURCES}/../Frameworks"
|
||||||
|
|
||||||
|
# Copy libz into the app bundle
|
||||||
|
cp /opt/local/lib/libz.1.dylib "${RESOURCES}/../Frameworks"
|
||||||
|
|
||||||
# Copy libtiff into the app bundle
|
# Copy libtiff into the app bundle
|
||||||
cp /opt/local/lib/libtiff.5.dylib "${RESOURCES}/../Frameworks"
|
cp /opt/local/lib/libtiff.5.dylib "${RESOURCES}/../Frameworks"
|
||||||
|
|
||||||
@@ -182,30 +188,66 @@ find -E "${CONTENTS}" -type f -regex '.*/(rawtherapee-cli|rawtherapee|.*\.(dylib
|
|||||||
done
|
done
|
||||||
|
|
||||||
msg "Registering @loader_path into the executable:"
|
msg "Registering @loader_path into the executable:"
|
||||||
echo " install_name_tool -add_rpath @loader_path/../Frameworks '${EXECUTABLE}'" | bash -v
|
echo " install_name_tool -add_rpath @executable_path/../../Frameworks '${EXECUTABLE}'" | bash -v
|
||||||
echo " install_name_tool -add_rpath @loader_path/../Frameworks '${EXECUTABLE}-cli'" | bash -v
|
echo " install_name_tool -add_rpath @loader_path/../Frameworks '${EXECUTABLE}-cli'" | bash -v
|
||||||
|
|
||||||
msg "Installing required application bundle files:"
|
msg "Installing required application bundle files:"
|
||||||
PROJECT_SOURCE_DATA_DIR="${PROJECT_SOURCE_DIR}/tools/osx"
|
PROJECT_SOURCE_DATA_DIR="${PROJECT_SOURCE_DIR}/tools/osx"
|
||||||
|
ditto "${PROJECT_SOURCE_DIR}/build/Resources" "${RESOURCES}"
|
||||||
# Executable loader
|
# Executable loader
|
||||||
# Note: executable is renamed to 'rawtherapee-bin'.
|
# Note: executable is renamed to 'rawtherapee-bin'.
|
||||||
mv "${MACOS}/rawtherapee" "${MACOS}/rawtherapee-bin"
|
mkdir "${MACOS}/bin"
|
||||||
|
mv "${MACOS}/rawtherapee" "${MACOS}/bin/rawtherapee-bin"
|
||||||
install -m 0755 "${PROJECT_SOURCE_DATA_DIR}/executable_loader.in" "${MACOS}/rawtherapee"
|
install -m 0755 "${PROJECT_SOURCE_DATA_DIR}/executable_loader.in" "${MACOS}/rawtherapee"
|
||||||
# App bundle resources
|
# App bundle resources
|
||||||
cp "${PROJECT_SOURCE_DATA_DIR}/"{rawtherapee,profile}.icns "${RESOURCES}"
|
cp "${PROJECT_SOURCE_DATA_DIR}/"{rawtherapee,profile}.icns "${RESOURCES}"
|
||||||
cp "${PROJECT_SOURCE_DATA_DIR}/PkgInfo" "${CONTENTS}"
|
cp "${PROJECT_SOURCE_DATA_DIR}/PkgInfo" "${CONTENTS}"
|
||||||
install -m 0644 "${PROJECT_SOURCE_DATA_DIR}/Info.plist.in" "${CONTENTS}/Info.plist"
|
install -m 0644 "${PROJECT_SOURCE_DATA_DIR}/Info.plist.in" "${CONTENTS}/Info.plist"
|
||||||
|
install -m 0644 "${PROJECT_SOURCE_DATA_DIR}/Info.plist-bin.in" "${CONTENTS}/MacOS/bin/Info.plist"
|
||||||
sed -i "" -e "s|@version@|${PROJECT_FULL_VERSION}|
|
sed -i "" -e "s|@version@|${PROJECT_FULL_VERSION}|
|
||||||
s|@shortVersion@|${PROJECT_VERSION}|
|
s|@shortVersion@|${PROJECT_VERSION}|
|
||||||
s|@arch@|${arch}|" \
|
s|@arch@|${arch}|" \
|
||||||
"${CONTENTS}/Info.plist"
|
"${CONTENTS}/Info.plist"
|
||||||
plutil -convert binary1 "${CONTENTS}/Info.plist"
|
plutil -convert binary1 "${CONTENTS}/Info.plist"
|
||||||
|
plutil -convert binary1 "${CONTENTS}/MacOS/bin/Info.plist"
|
||||||
# Sign the app
|
# Sign the app
|
||||||
CODESIGNID="$(cmake .. -LA -N | grep "CODESIGNID" | cut -d "=" -f2)"
|
CODESIGNID="$(cmake .. -LA -N | grep "CODESIGNID" | cut -d "=" -f2)"
|
||||||
codesign --deep --force -v -s "${CODESIGNID}" --timestamp "${APP}"
|
if ! test -z "$CODESIGNID" ; then
|
||||||
|
install -m 0644 "${PROJECT_SOURCE_DATA_DIR}/rt.entitlements" "${CONTENTS}/Entitlements.plist"
|
||||||
|
plutil -convert binary1 "${CONTENTS}/Entitlements.plist"
|
||||||
|
install -m 0644 "${PROJECT_SOURCE_DATA_DIR}/rt-bin.entitlements" "${CONTENTS}/MacOS/bin/Entitlements.plist"
|
||||||
|
plutil -convert binary1 "${CONTENTS}/MacOS/bin/Entitlements.plist"
|
||||||
|
codesign -v -s "${CODESIGNID}" -i "com.rawtherapee.rawtherapee-bin" --timestamp -o runtime --entitlements "${APP}/Contents/MacOS/bin/Entitlements.plist" "${APP}/Contents/MacOS/bin/rawtherapee-bin"
|
||||||
|
codesign --deep --preserve-metadata=identifier,entitlements,runtime --strict -v -s "${CODESIGNID}" -i "com.rawtherapee.rawtherapee" --timestamp -o runtime --entitlements "${APP}/Contents/Entitlements.plist" "${APP}"
|
||||||
spctl -a -vvvv "${APP}"
|
spctl -a -vvvv "${APP}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Notarize the app
|
||||||
|
NOTARY="$(cmake .. -LA -N | grep "NOTARY" | cut -d "=" -f2)"
|
||||||
|
if ! test -z "$NOTARY" ; then
|
||||||
|
ditto -c -k --sequesterRsrc --keepParent "${APP}" "${APP}.zip"
|
||||||
|
uuid=`xcrun altool --notarize-app --primary-bundle-id "com.rawtherapee.rawtherapee" ${NOTARY} --file "${APP}.zip" 2>&1 | grep 'RequestUUID' | awk '{ print $3 }'`
|
||||||
|
echo "Result= $uuid" # Display identifier string
|
||||||
|
sleep 15
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
fullstatus=`xcrun altool --notarization-info "$uuid" ${NOTARY} 2>&1` # get the status
|
||||||
|
status1=`echo "$fullstatus" | grep 'Status\:' | awk '{ print $2 }'`
|
||||||
|
if [ "$status1" = "success" ]; then
|
||||||
|
xcrun stapler staple *app # staple the ticket
|
||||||
|
xcrun stapler validate -v *app
|
||||||
|
echo "Notarization success"
|
||||||
|
break
|
||||||
|
elif [ "$status1" = "in" ]; then
|
||||||
|
echo "Notarization still in progress, sleeping for 15 seconds and trying again"
|
||||||
|
sleep 15
|
||||||
|
else
|
||||||
|
echo "Notarization failed fullstatus below"
|
||||||
|
echo "$fullstatus"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
function CreateDmg {
|
function CreateDmg {
|
||||||
local srcDir="$(mktemp -dt $$)"
|
local srcDir="$(mktemp -dt $$)"
|
||||||
@@ -234,7 +276,36 @@ function CreateDmg {
|
|||||||
hdiutil create -format UDBZ -fs HFS+ -srcdir "${srcDir}" -volname "${PROJECT_NAME}_${PROJECT_FULL_VERSION}" "${dmg_name}.dmg"
|
hdiutil create -format UDBZ -fs HFS+ -srcdir "${srcDir}" -volname "${PROJECT_NAME}_${PROJECT_FULL_VERSION}" "${dmg_name}.dmg"
|
||||||
|
|
||||||
# Sign disk image
|
# Sign disk image
|
||||||
|
if ! test -z "$CODESIGNID" ; then
|
||||||
codesign --deep --force -v -s "${CODESIGNID}" --timestamp "${dmg_name}.dmg"
|
codesign --deep --force -v -s "${CODESIGNID}" --timestamp "${dmg_name}.dmg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Notarize the dmg
|
||||||
|
if ! test -z "$NOTARY" ; then
|
||||||
|
zip "${dmg_name}.dmg.zip" "${dmg_name}.dmg"
|
||||||
|
uuid=`xcrun altool --notarize-app --primary-bundle-id "com.rawtherapee" ${NOTARY} --file "${dmg_name}.dmg.zip" 2>&1 | grep 'RequestUUID' | awk '{ print $3 }'`
|
||||||
|
echo "dmg Result= $uuid" # Display identifier string
|
||||||
|
sleep 15
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
fullstatus=`xcrun altool --notarization-info "$uuid" ${NOTARY} 2>&1` # get the status
|
||||||
|
status1=`echo "$fullstatus" | grep 'Status\:' | awk '{ print $2 }'`
|
||||||
|
if [ "$status1" = "success" ]; then
|
||||||
|
xcrun stapler staple "${dmg_name}.dmg" # staple the ticket
|
||||||
|
xcrun stapler validate -v "${dmg_name}.dmg"
|
||||||
|
echo "dmg Notarization success"
|
||||||
|
break
|
||||||
|
elif [ "$status1" = "in" ]; then
|
||||||
|
echo "dmg Notarization still in progress, sleeping for 15 seconds and trying again"
|
||||||
|
sleep 15
|
||||||
|
else
|
||||||
|
echo "dmg Notarization failed fullstatus below"
|
||||||
|
echo "$fullstatus"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Zip disk image for redistribution
|
# Zip disk image for redistribution
|
||||||
zip "${dmg_name}.zip" "${dmg_name}.dmg" AboutThisBuild.txt
|
zip "${dmg_name}.zip" "${dmg_name}.dmg" AboutThisBuild.txt
|
||||||
|
|||||||
8
tools/osx/rt-bin.entitlements
Normal file
8
tools/osx/rt-bin.entitlements
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.inherit</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
20
tools/osx/rt.entitlements
Normal file
20
tools/osx/rt.entitlements
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>application-identifier</key>
|
||||||
|
<string>com.rawtherapee.rawtherapee</string>
|
||||||
|
<key>com.apple.security.temporary-exception.files.absolute-path.read-write</key>
|
||||||
|
<array>
|
||||||
|
<string>"/"</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.downloads.read-write</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Reference in New Issue
Block a user