实现另外一种hdrplus
parent
5213b007d6
commit
78b00b42e9
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
// MACHINE GENERATED -- DO NOT EDIT
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
struct halide_filter_metadata_t;
|
||||||
|
void halide_register_argv_and_metadata(
|
||||||
|
int (*filter_argv_call)(void **),
|
||||||
|
const struct halide_filter_metadata_t *filter_metadata,
|
||||||
|
const char * const *extra_key_value_pairs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern int hdrplus_pipeline_argv(void **args);
|
||||||
|
extern const struct halide_filter_metadata_t *hdrplus_pipeline_metadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||||
|
extern "C" const char * const *HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC();
|
||||||
|
#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||||
|
|
||||||
|
namespace halide_nsreg_hdrplus_pipeline {
|
||||||
|
namespace {
|
||||||
|
struct Registerer {
|
||||||
|
Registerer() {
|
||||||
|
#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||||
|
halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC());
|
||||||
|
#else
|
||||||
|
halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), nullptr);
|
||||||
|
#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static Registerer registerer;
|
||||||
|
} // namespace
|
||||||
|
} // halide_nsreg_hdrplus_pipeline
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
// MACHINE GENERATED -- DO NOT EDIT
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
struct halide_filter_metadata_t;
|
||||||
|
void halide_register_argv_and_metadata(
|
||||||
|
int (*filter_argv_call)(void **),
|
||||||
|
const struct halide_filter_metadata_t *filter_metadata,
|
||||||
|
const char * const *extra_key_value_pairs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern int hdrplus_pipeline_argv(void **args);
|
||||||
|
extern const struct halide_filter_metadata_t *hdrplus_pipeline_metadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||||
|
extern "C" const char * const *HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC();
|
||||||
|
#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||||
|
|
||||||
|
namespace halide_nsreg_hdrplus_pipeline {
|
||||||
|
namespace {
|
||||||
|
struct Registerer {
|
||||||
|
Registerer() {
|
||||||
|
#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||||
|
halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC());
|
||||||
|
#else
|
||||||
|
halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), nullptr);
|
||||||
|
#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static Registerer registerer;
|
||||||
|
} // namespace
|
||||||
|
} // halide_nsreg_hdrplus_pipeline
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef __HDRPLUS__
|
||||||
|
#define __HDRPLUS__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <opencv2/opencv.hpp> // all opencv header
|
||||||
|
|
||||||
|
int doHdrPlus(const std::string& dir_path, const std::string& out_name, const std::vector<std::string>& in_names);
|
||||||
|
|
||||||
|
bool doHdrPlus(const std::vector< std::vector<uint8_t> >& images, cv::Mat& mat);
|
||||||
|
|
||||||
|
#endif // __HDRPLUS__
|
@ -0,0 +1,39 @@
|
|||||||
|
#include "Burst.h"
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<uint16_t> Burst::ToBuffer() const {
|
||||||
|
if (Raws.empty()) {
|
||||||
|
return Halide::Runtime::Buffer<uint16_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<uint16_t> result(GetWidth(), GetHeight(),
|
||||||
|
Raws.size());
|
||||||
|
for (int i = 0; i < Raws.size(); ++i) {
|
||||||
|
auto resultSlice = result.sliced(2, i);
|
||||||
|
Raws[i].CopyToBuffer(resultSlice);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Burst::CopyToBuffer(Halide::Runtime::Buffer<uint16_t> &buffer) const {
|
||||||
|
buffer.copy_from(ToBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<RawImage> Burst::LoadRaws(const std::vector< std::vector<uint8_t> >& images) {
|
||||||
|
std::vector<RawImage> result;
|
||||||
|
for (const auto &img : images) {
|
||||||
|
result.emplace_back(&img[0], img.size());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<RawImage> Burst::LoadRaws(const std::string &dirPath,
|
||||||
|
std::vector<std::string> &inputs) {
|
||||||
|
std::vector<RawImage> result;
|
||||||
|
for (const auto &input : inputs) {
|
||||||
|
const std::string img_path = dirPath + "/" + input;
|
||||||
|
result.emplace_back(img_path);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RawImage &Burst::GetRaw(const size_t i) const { return this->Raws[i]; }
|
@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "InputSource.h"
|
||||||
|
|
||||||
|
#include <hdrplus_pipeline.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Burst {
|
||||||
|
public:
|
||||||
|
Burst(std::string dir_path, std::vector<std::string> inputs)
|
||||||
|
: Dir(std::move(dir_path)), Inputs(std::move(inputs)),
|
||||||
|
Raws(LoadRaws(Dir, Inputs))
|
||||||
|
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Burst(const std::vector< std::vector<uint8_t> >& images)
|
||||||
|
: Raws(LoadRaws(images))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~Burst() = default;
|
||||||
|
|
||||||
|
Burst(const Burst& src)
|
||||||
|
{
|
||||||
|
this->Dir = src.Dir;
|
||||||
|
this->Inputs = src.Inputs;
|
||||||
|
this->Raws = src.Raws;
|
||||||
|
int aa = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetWidth() const { return Raws.empty() ? -1 : Raws[0].GetWidth(); }
|
||||||
|
|
||||||
|
int GetHeight() const { return Raws.empty() ? -1 : Raws[0].GetHeight(); }
|
||||||
|
|
||||||
|
int GetBlackLevel() const
|
||||||
|
{
|
||||||
|
return Raws.empty() ? -1 : Raws[0].GetScalarBlackLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetWhiteLevel() const {
|
||||||
|
return Raws.empty() ? -1 : Raws[0].GetWhiteLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
WhiteBalance GetWhiteBalance() const {
|
||||||
|
return Raws.empty() ? WhiteBalance{-1, -1, -1, -1}
|
||||||
|
: Raws[0].GetWhiteBalance();
|
||||||
|
}
|
||||||
|
|
||||||
|
CfaPattern GetCfaPattern() const {
|
||||||
|
return Raws.empty() ? CfaPattern::CFA_UNKNOWN : Raws[0].GetCfaPattern();
|
||||||
|
}
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<float> GetColorCorrectionMatrix() const {
|
||||||
|
return Raws.empty() ? Halide::Runtime::Buffer<float>()
|
||||||
|
: Raws[0].GetColorCorrectionMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<uint16_t> ToBuffer() const;
|
||||||
|
|
||||||
|
void CopyToBuffer(Halide::Runtime::Buffer<uint16_t> &buffer) const;
|
||||||
|
|
||||||
|
const RawImage &GetRaw(const size_t i) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string Dir;
|
||||||
|
std::vector<std::string> Inputs;
|
||||||
|
std::vector<RawImage> Raws;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::vector<RawImage> LoadRaws(const std::string &dirPath,
|
||||||
|
std::vector<std::string> &inputs);
|
||||||
|
static std::vector<RawImage> LoadRaws(const std::vector< std::vector<uint8_t> >& images);
|
||||||
|
};
|
@ -0,0 +1,137 @@
|
|||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// #define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
// #include <include/stb_image_write.h>
|
||||||
|
|
||||||
|
#include <hdrplus_pipeline.h>
|
||||||
|
#include "Burst.h"
|
||||||
|
|
||||||
|
#include <include/HDRPlus.h>
|
||||||
|
|
||||||
|
extern "C" void halide_register_argv_and_metadata(
|
||||||
|
int (*filter_argv_call)(void **),
|
||||||
|
const struct halide_filter_metadata_t *filter_metadata,
|
||||||
|
const char *const *extra_key_value_pairs) {
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* HDRPlus Class -- Houses file I/O, defines pipeline attributes and calls
|
||||||
|
* processes main stages of the pipeline.
|
||||||
|
*/
|
||||||
|
class HDRPlus {
|
||||||
|
const Burst &burst;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const Compression c;
|
||||||
|
const Gain g;
|
||||||
|
|
||||||
|
HDRPlus(Burst& burst, const Compression c, const Gain g)
|
||||||
|
: burst(burst), c(c), g(g)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<uint8_t> process() {
|
||||||
|
const int width = burst.GetWidth();
|
||||||
|
const int height = burst.GetHeight();
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<uint8_t> output_img(3, width, height);
|
||||||
|
|
||||||
|
std::cerr << "Black point: " << burst.GetBlackLevel() << std::endl;
|
||||||
|
std::cerr << "White point: " << burst.GetWhiteLevel() << std::endl;
|
||||||
|
|
||||||
|
const WhiteBalance wb = burst.GetWhiteBalance();
|
||||||
|
std::cerr << "RGGB: " << wb.r << " " << wb.g0 << " " << wb.g1 << " " << wb.b
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<uint16_t> imgs = burst.ToBuffer();
|
||||||
|
if (imgs.dimensions() != 3 || imgs.extent(2) < 2) {
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"The input of HDRPlus must be a 3-dimensional buffer with at least "
|
||||||
|
"two channels.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const int cfa_pattern = static_cast<int>(burst.GetCfaPattern());
|
||||||
|
auto ccm = burst.GetColorCorrectionMatrix();
|
||||||
|
hdrplus_pipeline(imgs, burst.GetBlackLevel(), burst.GetWhiteLevel(), wb.r,
|
||||||
|
wb.g0, wb.g1, wb.b, cfa_pattern, ccm, c, g, output_img);
|
||||||
|
|
||||||
|
// transpose to account for interleaved layout
|
||||||
|
output_img.transpose(0, 1);
|
||||||
|
output_img.transpose(1, 2);
|
||||||
|
|
||||||
|
return output_img;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static bool save_png(const std::string &dir_path, const std::string &img_name,
|
||||||
|
const Halide::Runtime::Buffer<uint8_t> &img) {
|
||||||
|
const std::string img_path = dir_path + "/" + img_name;
|
||||||
|
|
||||||
|
const int stride_in_bytes = img.width() * img.channels();
|
||||||
|
if (!stbi_write_png(img_path.c_str(), img.width(), img.height(),
|
||||||
|
img.channels(), img.data(), stride_in_bytes)) {
|
||||||
|
std::cerr << "Unable to write output image '" << img_name << "'"
|
||||||
|
<< std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
bool doHdrPlus(const std::vector< std::vector<uint8_t> >& images, cv::Mat& mat)
|
||||||
|
{
|
||||||
|
Compression c = 3.8f;
|
||||||
|
Gain g = 1.1f;
|
||||||
|
|
||||||
|
Burst burst(images);
|
||||||
|
|
||||||
|
HDRPlus hdr_plus(burst, c, g);
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<uint8_t> outputHdr = hdr_plus.process();
|
||||||
|
|
||||||
|
int width = outputHdr.width();
|
||||||
|
int height = outputHdr.height();
|
||||||
|
int channels = outputHdr.channels();
|
||||||
|
int jch = 0;
|
||||||
|
mat = cv::Mat::zeros(height, width, CV_8UC3);
|
||||||
|
for (int i = 0; i < height; ++i)
|
||||||
|
{
|
||||||
|
jch = 0;
|
||||||
|
for (int j = 0; j < width; ++j)
|
||||||
|
{
|
||||||
|
for (int n = 0; n < channels; ++n)
|
||||||
|
{
|
||||||
|
mat.at<uchar>(i, jch + n) = (uchar)outputHdr(j, i, n);
|
||||||
|
}
|
||||||
|
jch += channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!HDRPlus::save_png(dir_path, out_name, output)) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int doHdrPlus(const std::string& dir_path, const std::string& out_name, const std::vector<std::string>& in_names) {
|
||||||
|
|
||||||
|
|
||||||
|
Compression c = 3.8f;
|
||||||
|
Gain g = 1.1f;
|
||||||
|
|
||||||
|
Burst burst(dir_path, in_names);
|
||||||
|
|
||||||
|
HDRPlus hdr_plus(burst, c, g);
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<uint8_t> output = hdr_plus.process();
|
||||||
|
|
||||||
|
if (!HDRPlus::save_png(dir_path, out_name, output)) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,152 @@
|
|||||||
|
#include "InputSource.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "LibRaw2DngConverter.h"
|
||||||
|
|
||||||
|
RawImage::RawImage(const std::string &path)
|
||||||
|
: Path(path), RawProcessor(std::make_shared<LibRaw>()) {
|
||||||
|
// TODO: Check LibRaw parametres.
|
||||||
|
// RawProcessor->imgdata.params.X = Y;
|
||||||
|
|
||||||
|
std::cerr << "Opening " << path << std::endl;
|
||||||
|
if (int err = RawProcessor->open_file(path.c_str())) {
|
||||||
|
std::cerr << "Cannot open file " << path
|
||||||
|
<< " error: " << libraw_strerror(err) << std::endl;
|
||||||
|
#if 0
|
||||||
|
throw std::runtime_error("Error opening " + path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (int err = RawProcessor->unpack()) {
|
||||||
|
std::cerr << "Cannot unpack file " << path
|
||||||
|
<< " error: " << libraw_strerror(err) << std::endl;
|
||||||
|
#if 0
|
||||||
|
throw std::runtime_error("Error opening " + path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (int ret = RawProcessor->raw2image()) {
|
||||||
|
std::cerr << "Cannot do raw2image on " << path
|
||||||
|
<< " error: " << libraw_strerror(ret) << std::endl;
|
||||||
|
#if 0
|
||||||
|
throw std::runtime_error("Error opening " + path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RawImage::RawImage(const uint8_t* data, size_t length)
|
||||||
|
: RawProcessor(std::make_shared<LibRaw>())
|
||||||
|
{
|
||||||
|
std::cerr << "Opening raw from memory" << std::endl;
|
||||||
|
if (int err = RawProcessor->open_buffer((void *)data, length)) {
|
||||||
|
std::cerr << "Cannot open raw from memory" << " error: " << libraw_strerror(err) << std::endl;
|
||||||
|
#if 0
|
||||||
|
throw std::runtime_error("Error opening raw");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (int err = RawProcessor->unpack()) {
|
||||||
|
std::cerr << "Cannot unpack raw from memory " << " error: " << libraw_strerror(err) << std::endl;
|
||||||
|
#if 0
|
||||||
|
throw std::runtime_error("Error opening " + path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (int ret = RawProcessor->raw2image()) {
|
||||||
|
std::cerr << "Cannot do raw2image" << " error: " << libraw_strerror(ret) << std::endl;
|
||||||
|
#if 0
|
||||||
|
throw std::runtime_error("Error opening " + path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WhiteBalance RawImage::GetWhiteBalance() const {
|
||||||
|
const auto coeffs = RawProcessor->imgdata.color.cam_mul;
|
||||||
|
// Scale multipliers to green channel
|
||||||
|
const float r = coeffs[0] / coeffs[1];
|
||||||
|
const float g0 = 1.f; // same as coeffs[1] / coeffs[1];
|
||||||
|
const float g1 = 1.f;
|
||||||
|
const float b = coeffs[2] / coeffs[1];
|
||||||
|
return WhiteBalance{r, g0, g1, b};
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawImage::CopyToBuffer(Halide::Runtime::Buffer<uint16_t> &buffer) const {
|
||||||
|
const auto image_data = (uint16_t *)RawProcessor->imgdata.rawdata.raw_image;
|
||||||
|
const auto raw_width = RawProcessor->imgdata.rawdata.sizes.raw_width;
|
||||||
|
const auto raw_height = RawProcessor->imgdata.rawdata.sizes.raw_height;
|
||||||
|
const auto top = RawProcessor->imgdata.rawdata.sizes.top_margin;
|
||||||
|
const auto left = RawProcessor->imgdata.rawdata.sizes.left_margin;
|
||||||
|
Halide::Runtime::Buffer<uint16_t> raw_buffer(image_data, raw_width,
|
||||||
|
raw_height);
|
||||||
|
buffer.copy_from(raw_buffer.translated({-left, -top}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawImage::WriteDng(const std::string &output_path,
|
||||||
|
const Halide::Runtime::Buffer<uint16_t> &buffer) const {
|
||||||
|
LibRaw2DngConverter converter(*this);
|
||||||
|
converter.SetBuffer(buffer);
|
||||||
|
converter.Write(output_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<float, 4> RawImage::GetBlackLevel() const {
|
||||||
|
// See https://www.libraw.org/node/2471
|
||||||
|
const auto raw_color = RawProcessor->imgdata.color;
|
||||||
|
const auto base_black_level = static_cast<float>(raw_color.black);
|
||||||
|
|
||||||
|
std::array<float, 4> black_level = {
|
||||||
|
base_black_level + static_cast<float>(raw_color.cblack[0]),
|
||||||
|
base_black_level + static_cast<float>(raw_color.cblack[1]),
|
||||||
|
base_black_level + static_cast<float>(raw_color.cblack[2]),
|
||||||
|
base_black_level + static_cast<float>(raw_color.cblack[3])};
|
||||||
|
|
||||||
|
if (raw_color.cblack[4] == 2 && raw_color.cblack[5] == 2) {
|
||||||
|
for (int x = 0; x < raw_color.cblack[4]; ++x) {
|
||||||
|
for (int y = 0; y < raw_color.cblack[5]; ++y) {
|
||||||
|
const auto index = y * 2 + x;
|
||||||
|
black_level[index] = raw_color.cblack[6 + index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return black_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RawImage::GetScalarBlackLevel() const {
|
||||||
|
const auto black_level = GetBlackLevel();
|
||||||
|
return static_cast<int>(
|
||||||
|
*std::min_element(black_level.begin(), black_level.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RawImage::GetCfaPatternString() const {
|
||||||
|
static const std::unordered_map<char, char> CDESC_TO_CFA = {
|
||||||
|
{'R', 0}, {'G', 1}, {'B', 2}, {'r', 0}, {'g', 1}, {'b', 2}};
|
||||||
|
const auto &cdesc = RawProcessor->imgdata.idata.cdesc;
|
||||||
|
return {CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(0, 0)]),
|
||||||
|
CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(0, 1)]),
|
||||||
|
CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(1, 0)]),
|
||||||
|
CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(1, 1)])};
|
||||||
|
}
|
||||||
|
|
||||||
|
CfaPattern RawImage::GetCfaPattern() const {
|
||||||
|
const auto cfa_pattern = GetCfaPatternString();
|
||||||
|
if (cfa_pattern == std::string{0, 1, 1, 2}) {
|
||||||
|
return CfaPattern::CFA_RGGB;
|
||||||
|
} else if (cfa_pattern == std::string{1, 0, 2, 1}) {
|
||||||
|
return CfaPattern::CFA_GRBG;
|
||||||
|
} else if (cfa_pattern == std::string{2, 1, 1, 0}) {
|
||||||
|
return CfaPattern::CFA_BGGR;
|
||||||
|
} else if (cfa_pattern == std::string{1, 2, 0, 1}) {
|
||||||
|
return CfaPattern::CFA_GBRG;
|
||||||
|
}
|
||||||
|
throw std::invalid_argument("Unsupported CFA pattern: " + cfa_pattern);
|
||||||
|
return CfaPattern::CFA_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<float> RawImage::GetColorCorrectionMatrix() const {
|
||||||
|
const auto raw_color = RawProcessor->imgdata.color;
|
||||||
|
Halide::Runtime::Buffer<float> ccm(3, 3);
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
ccm(i, j) = raw_color.rgb_cam[j][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ccm;
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <libraw/libraw.h>
|
||||||
|
|
||||||
|
#include "finish.h"
|
||||||
|
#include <HalideBuffer.h>
|
||||||
|
|
||||||
|
class RawImage {
|
||||||
|
public:
|
||||||
|
explicit RawImage(const std::string &path);
|
||||||
|
explicit RawImage(const uint8_t* data, size_t length);
|
||||||
|
|
||||||
|
~RawImage() = default;
|
||||||
|
|
||||||
|
int GetWidth() const { return RawProcessor->imgdata.rawdata.sizes.width; }
|
||||||
|
|
||||||
|
int GetHeight() const { return RawProcessor->imgdata.rawdata.sizes.height; }
|
||||||
|
|
||||||
|
int GetScalarBlackLevel() const;
|
||||||
|
|
||||||
|
std::array<float, 4> GetBlackLevel() const;
|
||||||
|
|
||||||
|
int GetWhiteLevel() const { return RawProcessor->imgdata.color.maximum; }
|
||||||
|
|
||||||
|
WhiteBalance GetWhiteBalance() const;
|
||||||
|
|
||||||
|
std::string GetCfaPatternString() const;
|
||||||
|
CfaPattern GetCfaPattern() const;
|
||||||
|
|
||||||
|
Halide::Runtime::Buffer<float> GetColorCorrectionMatrix() const;
|
||||||
|
|
||||||
|
void CopyToBuffer(Halide::Runtime::Buffer<uint16_t> &buffer) const;
|
||||||
|
|
||||||
|
// Writes current RawImage as DNG. If buffer was provided, then use it instead
|
||||||
|
// of internal buffer.
|
||||||
|
void WriteDng(const std::string &path,
|
||||||
|
const Halide::Runtime::Buffer<uint16_t> &buffer = {}) const;
|
||||||
|
|
||||||
|
std::shared_ptr<LibRaw> GetRawProcessor() const { return RawProcessor; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string Path;
|
||||||
|
std::shared_ptr<LibRaw> RawProcessor;
|
||||||
|
};
|
@ -0,0 +1,95 @@
|
|||||||
|
#include "LibRaw2DngConverter.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <libraw/libraw.h>
|
||||||
|
|
||||||
|
#include "InputSource.h"
|
||||||
|
|
||||||
|
LibRaw2DngConverter::LibRaw2DngConverter(const RawImage &raw)
|
||||||
|
: OutputStream(), Raw(raw),
|
||||||
|
Tiff(SetTiffFields(
|
||||||
|
TiffPtr(TIFFStreamOpen("", &OutputStream), TIFFClose))) {}
|
||||||
|
|
||||||
|
LibRaw2DngConverter::TiffPtr
|
||||||
|
LibRaw2DngConverter::SetTiffFields(LibRaw2DngConverter::TiffPtr tiff_ptr) {
|
||||||
|
const auto RawProcessor = Raw.GetRawProcessor();
|
||||||
|
const auto raw_color = RawProcessor->imgdata.color;
|
||||||
|
|
||||||
|
const uint16_t bayer_pattern_dimensions[] = {2, 2};
|
||||||
|
|
||||||
|
const auto tiff = tiff_ptr.get();
|
||||||
|
TIFFSetField(tiff, TIFFTAG_DNGVERSION, "\01\04\00\00");
|
||||||
|
TIFFSetField(tiff, TIFFTAG_DNGBACKWARDVERSION, "\01\04\00\00");
|
||||||
|
TIFFSetField(tiff, TIFFTAG_SUBFILETYPE, 0);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_CFAREPEATPATTERNDIM, &bayer_pattern_dimensions);
|
||||||
|
|
||||||
|
const std::string cfa = Raw.GetCfaPatternString();
|
||||||
|
TIFFSetField(tiff, TIFFTAG_CFAPATTERN, cfa.c_str());
|
||||||
|
|
||||||
|
TIFFSetField(tiff, TIFFTAG_MAKE, "hdr-plus");
|
||||||
|
TIFFSetField(tiff, TIFFTAG_UNIQUECAMERAMODEL, "hdr-plus");
|
||||||
|
|
||||||
|
const std::array<float, 9> color_matrix = {
|
||||||
|
raw_color.cam_xyz[0][0], raw_color.cam_xyz[0][1], raw_color.cam_xyz[0][2],
|
||||||
|
raw_color.cam_xyz[1][0], raw_color.cam_xyz[1][1], raw_color.cam_xyz[1][2],
|
||||||
|
raw_color.cam_xyz[2][0], raw_color.cam_xyz[2][1], raw_color.cam_xyz[2][2],
|
||||||
|
};
|
||||||
|
TIFFSetField(tiff, TIFFTAG_COLORMATRIX1, 9, &color_matrix);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_CALIBRATIONILLUMINANT1, 21); // D65
|
||||||
|
|
||||||
|
const std::array<float, 3> as_shot_neutral = {
|
||||||
|
1.f / (raw_color.cam_mul[0] / raw_color.cam_mul[1]), 1.f,
|
||||||
|
1.f / (raw_color.cam_mul[2] / raw_color.cam_mul[1])};
|
||||||
|
TIFFSetField(tiff, TIFFTAG_ASSHOTNEUTRAL, 3, &as_shot_neutral);
|
||||||
|
|
||||||
|
TIFFSetField(tiff, TIFFTAG_CFALAYOUT, 1); // Rectangular (or square) layout
|
||||||
|
TIFFSetField(
|
||||||
|
tiff, TIFFTAG_CFAPLANECOLOR, 3,
|
||||||
|
"\00\01\02"); // RGB
|
||||||
|
// https://www.awaresystems.be/imaging/tiff/tifftags/cfaplanecolor.html
|
||||||
|
|
||||||
|
const std::array<float, 4> black_level = Raw.GetBlackLevel();
|
||||||
|
TIFFSetField(tiff, TIFFTAG_BLACKLEVEL, 4, &black_level);
|
||||||
|
|
||||||
|
static const uint32_t white_level = raw_color.maximum;
|
||||||
|
TIFFSetField(tiff, TIFFTAG_WHITELEVEL, 1, &white_level);
|
||||||
|
|
||||||
|
if (RawProcessor->imgdata.sizes.flip > 0) {
|
||||||
|
// Seems that LibRaw uses LibTIFF notation.
|
||||||
|
TIFFSetField(tiff, TIFFTAG_ORIENTATION, RawProcessor->imgdata.sizes.flip);
|
||||||
|
} else {
|
||||||
|
TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||||
|
}
|
||||||
|
return tiff_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibRaw2DngConverter::SetBuffer(
|
||||||
|
const Halide::Runtime::Buffer<uint16_t> &buffer) const {
|
||||||
|
const auto width = buffer.width();
|
||||||
|
const auto height = buffer.height();
|
||||||
|
const auto tiff = Tiff.get();
|
||||||
|
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width);
|
||||||
|
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height);
|
||||||
|
|
||||||
|
uint16_t *row_pointer = buffer.data();
|
||||||
|
for (int row = 0; row < height; row++) {
|
||||||
|
TIFFWriteScanline(tiff, row_pointer, row, 0);
|
||||||
|
row_pointer += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibRaw2DngConverter::Write(const std::string &path) const {
|
||||||
|
TIFFCheckpointDirectory(Tiff.get());
|
||||||
|
TIFFFlush(Tiff.get());
|
||||||
|
std::ofstream output(path, std::ofstream::binary);
|
||||||
|
output << OutputStream.str();
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <tiffio.h>
|
||||||
|
#include <tiffio.hxx>
|
||||||
|
|
||||||
|
#include <HalideBuffer.h>
|
||||||
|
|
||||||
|
class RawImage;
|
||||||
|
|
||||||
|
class LibRaw2DngConverter {
|
||||||
|
using TiffPtr = std::shared_ptr<TIFF>;
|
||||||
|
TiffPtr SetTiffFields(TiffPtr tiff_ptr);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LibRaw2DngConverter(const RawImage &raw);
|
||||||
|
|
||||||
|
void SetBuffer(const Halide::Runtime::Buffer<uint16_t> &buffer) const;
|
||||||
|
|
||||||
|
void Write(const std::string &path) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostringstream OutputStream;
|
||||||
|
const RawImage &Raw;
|
||||||
|
std::shared_ptr<TIFF> Tiff;
|
||||||
|
};
|
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef HDRPLUS_FINISH_H_
|
||||||
|
#define HDRPLUS_FINISH_H_
|
||||||
|
|
||||||
|
#include <hdrplus_pipeline.h>
|
||||||
|
|
||||||
|
template <class T = float> struct TypedWhiteBalance {
|
||||||
|
template <class TT>
|
||||||
|
explicit TypedWhiteBalance(const TypedWhiteBalance<TT> &other)
|
||||||
|
: r(other.r), g0(other.g0), g1(other.g1), b(other.b) {}
|
||||||
|
|
||||||
|
TypedWhiteBalance(T r, T g0, T g1, T b) : r(r), g0(g0), g1(g1), b(b) {}
|
||||||
|
|
||||||
|
T r;
|
||||||
|
T g0;
|
||||||
|
T g1;
|
||||||
|
T b;
|
||||||
|
};
|
||||||
|
|
||||||
|
using WhiteBalance = TypedWhiteBalance<float>;
|
||||||
|
|
||||||
|
typedef uint16_t BlackPoint;
|
||||||
|
typedef uint16_t WhitePoint;
|
||||||
|
|
||||||
|
typedef float Compression;
|
||||||
|
typedef float Gain;
|
||||||
|
|
||||||
|
enum class CfaPattern : int {
|
||||||
|
CFA_UNKNOWN = 0,
|
||||||
|
CFA_RGGB = 1,
|
||||||
|
CFA_GRBG = 2,
|
||||||
|
CFA_BGGR = 3,
|
||||||
|
CFA_GBRG = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue