Implement DNG gain map for LibRaw
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <glib/gstdio.h>
|
||||
#include <glibmm/convert.h>
|
||||
|
||||
#include "dnggainmap.h"
|
||||
#include "imagedata.h"
|
||||
#include "imagesource.h"
|
||||
#include "metadata.h"
|
||||
@@ -63,6 +65,154 @@ auto to_long(const Iterator &iter, Integer n = Integer{0}) -> decltype(
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience class for reading data from a metadata tag's bytes value.
|
||||
*
|
||||
* It maintains an offset. Data is read starting from the offset, then the
|
||||
* offset is advanced to the byte after the last byte read.
|
||||
*/
|
||||
class TagValueReader
|
||||
{
|
||||
using DataContainer = std::vector<Exiv2::byte>;
|
||||
using DataOffset = DataContainer::difference_type;
|
||||
|
||||
DataContainer data;
|
||||
DataOffset offset{0};
|
||||
Exiv2::ByteOrder defaultByteOrder;
|
||||
|
||||
/**
|
||||
* Reads a value at the current offset.
|
||||
*
|
||||
* @tparam T Value's type.
|
||||
* @tparam getter Function that interprets the data using a given byte order
|
||||
* and returns the value at a given location.
|
||||
* @return The value.
|
||||
*/
|
||||
template <typename T, T (&getter)(const Exiv2::byte *, Exiv2::ByteOrder)>
|
||||
T readValue()
|
||||
{
|
||||
T value = getter(data.data() + offset, defaultByteOrder);
|
||||
offset += sizeof(T);
|
||||
return value;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a reader for the given value with the given byte order.
|
||||
*
|
||||
* @param value The value.
|
||||
* @param defaultByteOrder The byte order of the value's data.
|
||||
*/
|
||||
TagValueReader(const Exiv2::Value &value, Exiv2::ByteOrder defaultByteOrder = Exiv2::bigEndian) :
|
||||
data(value.size()),
|
||||
defaultByteOrder(defaultByteOrder)
|
||||
{
|
||||
value.copy(data.data(), Exiv2::invalidByteOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value's size in bytes.
|
||||
*/
|
||||
std::size_t size() const
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current offset is at or beyond the end of the data.
|
||||
*/
|
||||
bool isEnd() const
|
||||
{
|
||||
return offset > 0 && static_cast<std::size_t>(offset) >= data.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a double from the current offset and advances the offset.
|
||||
*/
|
||||
double readDouble()
|
||||
{
|
||||
return readValue<double, Exiv2::getDouble>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a float from the current offset and advances the offset.
|
||||
*/
|
||||
float readFloat()
|
||||
{
|
||||
return readValue<float, Exiv2::getFloat>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned integer from the current offset and advances the
|
||||
* offset.
|
||||
*/
|
||||
std::uint32_t readUInt()
|
||||
{
|
||||
return readValue<std::uint32_t, Exiv2::getULong>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the offset.
|
||||
*/
|
||||
void seekAbsolute(DataOffset newOffset)
|
||||
{
|
||||
offset = newOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the offset by the given amount.
|
||||
*/
|
||||
void seekRelative(DataOffset offsetDifference)
|
||||
{
|
||||
offset += offsetDifference;
|
||||
}
|
||||
};
|
||||
|
||||
GainMap readGainMap(TagValueReader &reader)
|
||||
{
|
||||
reader.seekRelative(4); // skip 4 bytes as we know that the opcode 4 takes 4 byte
|
||||
reader.seekRelative(8); // skip 8 bytes as they don't interest us currently
|
||||
GainMap gainMap;
|
||||
gainMap.Top = reader.readUInt();
|
||||
gainMap.Left = reader.readUInt();
|
||||
gainMap.Bottom = reader.readUInt();
|
||||
gainMap.Right = reader.readUInt();
|
||||
gainMap.Plane = reader.readUInt();
|
||||
gainMap.Planes = reader.readUInt();
|
||||
gainMap.RowPitch = reader.readUInt();
|
||||
gainMap.ColPitch = reader.readUInt();
|
||||
gainMap.MapPointsV = reader.readUInt();
|
||||
gainMap.MapPointsH = reader.readUInt();
|
||||
gainMap.MapSpacingV = reader.readDouble();
|
||||
gainMap.MapSpacingH = reader.readDouble();
|
||||
gainMap.MapOriginV = reader.readDouble();
|
||||
gainMap.MapOriginH = reader.readDouble();
|
||||
gainMap.MapPlanes = reader.readUInt();
|
||||
const std::size_t n = static_cast<std::size_t>(gainMap.MapPointsV) * static_cast<std::size_t>(gainMap.MapPointsH) * static_cast<std::size_t>(gainMap.MapPlanes);
|
||||
gainMap.MapGain.reserve(n);
|
||||
for (std::size_t i = 0; i < n; ++i) {
|
||||
gainMap.MapGain.push_back(reader.readFloat());
|
||||
}
|
||||
return gainMap;
|
||||
}
|
||||
|
||||
void readOpcodesList(const Exiv2::Value &value, std::vector<GainMap> &gainMaps)
|
||||
{
|
||||
TagValueReader reader(value);
|
||||
std::uint32_t ntags = reader.readUInt(); // read the number of opcodes
|
||||
if (ntags >= reader.size() / 12) { // rough check for wrong value (happens for example with DNG files from DJI FC6310)
|
||||
return;
|
||||
}
|
||||
while (ntags-- && !reader.isEnd()) {
|
||||
unsigned opcode = reader.readUInt();
|
||||
if (opcode == 9 && gainMaps.size() < 4) {
|
||||
gainMaps.push_back(readGainMap(reader));
|
||||
} else {
|
||||
reader.seekRelative(8); // skip 8 bytes as they don't interest us currently
|
||||
reader.seekRelative(reader.readUInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace rtengine {
|
||||
@@ -675,6 +825,18 @@ FramesData::FramesData(const Glib::ustring &fname, time_t ts) :
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t dngVersion = 0;
|
||||
if (find_exif_tag("Exif.Image.DNGVersion") && pos->count() == 4) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
dngVersion = (dngVersion << 8) + static_cast<uint32_t>(to_long(pos, i));
|
||||
}
|
||||
}
|
||||
|
||||
// Read gain maps.
|
||||
if (dngVersion && (find_exif_tag("Exif.SubImage1.OpcodeList2") || find_exif_tag("Exif.Image.OpcodeList2"))) {
|
||||
readOpcodesList(pos->value(), gain_maps_);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
if (settings->verbose) {
|
||||
std::cerr << "EXIV2 ERROR: " << e.what() << std::endl;
|
||||
@@ -916,6 +1078,10 @@ void FramesData::fillBasicTags(Exiv2::ExifData &exif) const
|
||||
set_exif(exif, "Exif.Photo.DateTimeOriginal", buf);
|
||||
}
|
||||
|
||||
std::vector<GainMap> FramesData::getGainMaps() const
|
||||
{
|
||||
return gain_maps_;
|
||||
}
|
||||
|
||||
void FramesData::getDimensions(int &w, int &h) const
|
||||
{
|
||||
|
Reference in New Issue
Block a user