Commit 01074177 authored by Davis King's avatar Davis King

Upgraded the dng image format so it can natively store floating point

pixel types without any information loss.
parent 90668383
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "../uintn.h" #include "../uintn.h"
#include "../image_transforms/assign_image.h" #include "../image_transforms/assign_image.h"
#include <algorithm> #include <algorithm>
#include "../vectorstream.h"
namespace dlib namespace dlib
{ {
...@@ -576,180 +577,243 @@ namespace dlib ...@@ -576,180 +577,243 @@ namespace dlib
else else
image.clear(); image.clear();
typedef entropy_decoder::kernel_2a decoder_type; if (type != grayscale_float)
decoder_type decoder;
decoder.set_stream(in);
entropy_decoder_model<256,decoder_type>::kernel_5a edm(decoder);
unsigned long symbol;
rgb_pixel p_rgb;
rgb_alpha_pixel p_rgba;
hsi_pixel p_hsi;
switch (type)
{ {
case rgb_alpha_paeth: typedef entropy_decoder::kernel_2a decoder_type;
decoder_type decoder;
decoder.set_stream(in);
entropy_decoder_model<256,decoder_type>::kernel_5a edm(decoder);
unsigned long symbol;
rgb_pixel p_rgb;
rgb_alpha_pixel p_rgba;
hsi_pixel p_hsi;
switch (type)
{
case rgb_alpha_paeth:
for (long r = 0; r < image.nr(); ++r) for (long r = 0; r < image.nr(); ++r)
{
for (long c = 0; c < image.nc(); ++c)
{ {
p_rgba = predictor_rgb_alpha_paeth(image,r,c); for (long c = 0; c < image.nc(); ++c)
edm.decode(symbol); {
p_rgba.red += static_cast<unsigned char>(symbol); p_rgba = predictor_rgb_alpha_paeth(image,r,c);
edm.decode(symbol);
p_rgba.red += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgba.green += static_cast<unsigned char>(symbol); p_rgba.green += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgba.blue += static_cast<unsigned char>(symbol); p_rgba.blue += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgba.alpha += static_cast<unsigned char>(symbol); p_rgba.alpha += static_cast<unsigned char>(symbol);
assign_pixel(image[r][c],p_rgba); assign_pixel(image[r][c],p_rgba);
}
} }
} break;
break;
case rgb_alpha: case rgb_alpha:
for (long r = 0; r < image.nr(); ++r) for (long r = 0; r < image.nr(); ++r)
{
for (long c = 0; c < image.nc(); ++c)
{ {
p_rgba = predictor_rgb_alpha(image,r,c); for (long c = 0; c < image.nc(); ++c)
edm.decode(symbol); {
p_rgba.red += static_cast<unsigned char>(symbol); p_rgba = predictor_rgb_alpha(image,r,c);
edm.decode(symbol);
p_rgba.red += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgba.green += static_cast<unsigned char>(symbol); p_rgba.green += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgba.blue += static_cast<unsigned char>(symbol); p_rgba.blue += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgba.alpha += static_cast<unsigned char>(symbol); p_rgba.alpha += static_cast<unsigned char>(symbol);
assign_pixel(image[r][c],p_rgba); assign_pixel(image[r][c],p_rgba);
}
} }
} break;
break;
case rgb_paeth: case rgb_paeth:
for (long r = 0; r < image.nr(); ++r) for (long r = 0; r < image.nr(); ++r)
{
for (long c = 0; c < image.nc(); ++c)
{ {
p_rgb = predictor_rgb_paeth(image,r,c); for (long c = 0; c < image.nc(); ++c)
edm.decode(symbol); {
p_rgb.red += static_cast<unsigned char>(symbol); p_rgb = predictor_rgb_paeth(image,r,c);
edm.decode(symbol);
p_rgb.red += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgb.green += static_cast<unsigned char>(symbol); p_rgb.green += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgb.blue += static_cast<unsigned char>(symbol); p_rgb.blue += static_cast<unsigned char>(symbol);
assign_pixel(image[r][c],p_rgb); assign_pixel(image[r][c],p_rgb);
}
} }
} break;
break;
case rgb: case rgb:
for (long r = 0; r < image.nr(); ++r) for (long r = 0; r < image.nr(); ++r)
{
for (long c = 0; c < image.nc(); ++c)
{ {
p_rgb = predictor_rgb(image,r,c); for (long c = 0; c < image.nc(); ++c)
edm.decode(symbol); {
p_rgb.red += static_cast<unsigned char>(symbol); p_rgb = predictor_rgb(image,r,c);
edm.decode(symbol);
p_rgb.red += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgb.green += static_cast<unsigned char>(symbol); p_rgb.green += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_rgb.blue += static_cast<unsigned char>(symbol); p_rgb.blue += static_cast<unsigned char>(symbol);
assign_pixel(image[r][c],p_rgb); assign_pixel(image[r][c],p_rgb);
}
} }
} break;
break;
case hsi: case hsi:
for (long r = 0; r < image.nr(); ++r) for (long r = 0; r < image.nr(); ++r)
{
for (long c = 0; c < image.nc(); ++c)
{ {
p_hsi = predictor_hsi(image,r,c); for (long c = 0; c < image.nc(); ++c)
edm.decode(symbol); {
p_hsi.h += static_cast<unsigned char>(symbol); p_hsi = predictor_hsi(image,r,c);
edm.decode(symbol);
p_hsi.h += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_hsi.s += static_cast<unsigned char>(symbol); p_hsi.s += static_cast<unsigned char>(symbol);
edm.decode(symbol); edm.decode(symbol);
p_hsi.i += static_cast<unsigned char>(symbol); p_hsi.i += static_cast<unsigned char>(symbol);
assign_pixel(image[r][c],p_hsi); assign_pixel(image[r][c],p_hsi);
}
} }
} break;
break;
case grayscale: case grayscale:
{
unsigned char p;
for (long r = 0; r < image.nr(); ++r)
{ {
for (long c = 0; c < image.nc(); ++c) unsigned char p;
for (long r = 0; r < image.nr(); ++r)
{ {
edm.decode(symbol); for (long c = 0; c < image.nc(); ++c)
p = static_cast<unsigned char>(symbol); {
p += predictor_grayscale(image,r,c); edm.decode(symbol);
assign_pixel(image[r][c],p); p = static_cast<unsigned char>(symbol);
p += predictor_grayscale(image,r,c);
assign_pixel(image[r][c],p);
}
} }
} }
} break;
break;
case grayscale_16bit: case grayscale_16bit:
{
uint16 p;
for (long r = 0; r < image.nr(); ++r)
{ {
for (long c = 0; c < image.nc(); ++c) uint16 p;
for (long r = 0; r < image.nr(); ++r)
{ {
edm.decode(symbol); for (long c = 0; c < image.nc(); ++c)
p = static_cast<uint16>(symbol); {
p <<= 8; edm.decode(symbol);
edm.decode(symbol); p = static_cast<uint16>(symbol);
p |= static_cast<uint16>(symbol); p <<= 8;
edm.decode(symbol);
p += predictor_grayscale_16(image,r,c); p |= static_cast<uint16>(symbol);
assign_pixel(image[r][c],p);
p += predictor_grayscale_16(image,r,c);
assign_pixel(image[r][c],p);
}
} }
} }
} break;
break;
default: default:
throw image_load_error("corruption detected in the dng file");
} // switch (type)
edm.decode(symbol);
if (symbol != dng_magic_byte)
throw image_load_error("corruption detected in the dng file");
edm.decode(symbol);
if (symbol != dng_magic_byte)
throw image_load_error("corruption detected in the dng file");
edm.decode(symbol);
if (symbol != dng_magic_byte)
throw image_load_error("corruption detected in the dng file"); throw image_load_error("corruption detected in the dng file");
} // switch (type) edm.decode(symbol);
if (symbol != dng_magic_byte)
edm.decode(symbol); throw image_load_error("corruption detected in the dng file");
if (symbol != dng_magic_byte) }
throw image_load_error("corruption detected in the dng file"); else // if this is a grayscale_float type image
edm.decode(symbol); {
if (symbol != dng_magic_byte) std::vector<int64> man(image.size());
throw image_load_error("corruption detected in the dng file"); std::vector<char> expbuf;
edm.decode(symbol); // get the mantissa data
if (symbol != dng_magic_byte) for (unsigned long i = 0; i < man.size(); ++i)
throw image_load_error("corruption detected in the dng file"); deserialize(man[i], in);
edm.decode(symbol); // get the compressed exponent data
if (symbol != dng_magic_byte) deserialize(expbuf, in);
throw image_load_error("corruption detected in the dng file"); typedef entropy_decoder::kernel_2a decoder_type;
typedef entropy_decoder_model<256,decoder_type>::kernel_4a edm_exp_type;
vectorstream inexp(expbuf);
decoder_type decoder;
decoder.set_stream(inexp);
edm_exp_type edm_exp(decoder);
float_details prev;
unsigned long i = 0;
// fill out the image
for (long r = 0; r < image.nr(); ++r)
{
for (long c = 0; c < image.nc(); ++c)
{
unsigned long exp1, exp2;
edm_exp.decode(exp1);
edm_exp.decode(exp2);
float_details cur(man[i++],(exp2<<8) | exp1);
cur.exponent += prev.exponent;
cur.mantissa += prev.mantissa;
prev = cur;
// Only use long double precision if the target image contains long
// doubles because it's slower to use those.
if (!is_same_type<typename image_type::type,long double>::value)
{
double temp = cur;
assign_pixel(image[r][c],temp);
}
else
{
long double temp = cur;
assign_pixel(image[r][c],temp);
}
}
}
unsigned long symbol;
edm_exp.decode(symbol);
if (symbol != dng_magic_byte)
throw image_load_error("corruption detected in the dng file");
edm_exp.decode(symbol);
if (symbol != dng_magic_byte)
throw image_load_error("corruption detected in the dng file");
edm_exp.decode(symbol);
if (symbol != dng_magic_byte)
throw image_load_error("corruption detected in the dng file");
edm_exp.decode(symbol);
if (symbol != dng_magic_byte)
throw image_load_error("corruption detected in the dng file");
}
} }
catch (...) catch (...)
{ {
......
...@@ -20,7 +20,8 @@ namespace dlib ...@@ -20,7 +20,8 @@ namespace dlib
rgb_paeth, rgb_paeth,
rgb_alpha, rgb_alpha,
rgb_alpha_paeth, rgb_alpha_paeth,
grayscale_16bit grayscale_16bit,
grayscale_float
}; };
const unsigned long dng_magic_byte = 100; const unsigned long dng_magic_byte = 100;
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include "dng_shared.h" #include "dng_shared.h"
#include "../uintn.h" #include "../uintn.h"
#include "../dir_nav.h" #include "../dir_nav.h"
#include "../float_details.h"
#include "../vectorstream.h"
namespace dlib namespace dlib
{ {
...@@ -249,7 +251,9 @@ namespace dlib ...@@ -249,7 +251,9 @@ namespace dlib
false, false,
pixel_traits<typename image_type::type>::rgb_alpha, pixel_traits<typename image_type::type>::rgb_alpha,
false, false,
pixel_traits<typename image_type::type>::grayscale && sizeof(typename image_type::type) != 1 pixel_traits<typename image_type::type>::grayscale && sizeof(typename image_type::type) != 1 &&
!is_float_type<typename image_type::type>::value,
is_float_type<typename image_type::type>::value
>::value >::value
> >
struct save_dng_helper; struct save_dng_helper;
...@@ -257,6 +261,64 @@ namespace dlib ...@@ -257,6 +261,64 @@ namespace dlib
typedef entropy_encoder::kernel_2a encoder_type; typedef entropy_encoder::kernel_2a encoder_type;
typedef entropy_encoder_model<256,encoder_type>::kernel_5a eem_type; typedef entropy_encoder_model<256,encoder_type>::kernel_5a eem_type;
typedef entropy_encoder_model<256,encoder_type>::kernel_4a eem_exp_type;
template <typename image_type >
struct save_dng_helper<image_type, grayscale_float>
{
static void save_dng (
const image_type& image,
std::ostream& out
)
{
out.write("DNG",3);
unsigned long version = 1;
serialize(version,out);
unsigned long type = grayscale_float;
serialize(type,out);
serialize(image.nc(),out);
serialize(image.nr(),out);
// Write the compressed exponent data into expbuf. We will append it
// to the stream at the end of the loops.
std::vector<char> expbuf;
expbuf.reserve(image.size()*2);
vectorstream outexp(expbuf);
encoder_type encoder;
encoder.set_stream(outexp);
eem_exp_type eem_exp(encoder);
float_details prev;
for (long r = 0; r < image.nr(); ++r)
{
for (long c = 0; c < image.nc(); ++c)
{
float_details cur = image[r][c];
int16 exp = cur.exponent-prev.exponent;
int64 man = cur.mantissa-prev.mantissa;
prev = cur;
unsigned char ebyte1 = exp&0xFF;
unsigned char ebyte2 = exp>>8;
eem_exp.encode(ebyte1);
eem_exp.encode(ebyte2);
serialize(man, out);
}
}
// write out the magic byte to mark the end of the compressed data.
eem_exp.encode(dng_magic_byte);
eem_exp.encode(dng_magic_byte);
eem_exp.encode(dng_magic_byte);
eem_exp.encode(dng_magic_byte);
encoder.clear();
serialize(expbuf, out);
}
};
template <typename image_type > template <typename image_type >
struct save_dng_helper<image_type, grayscale_16bit> struct save_dng_helper<image_type, grayscale_16bit>
{ {
......
...@@ -90,11 +90,11 @@ namespace dlib ...@@ -90,11 +90,11 @@ namespace dlib
- image[0][0] will be in the upper left corner of the image. - image[0][0] will be in the upper left corner of the image.
- image[image.nr()-1][image.nc()-1] will be in the lower right - image[image.nr()-1][image.nc()-1] will be in the lower right
corner of the image. corner of the image.
- This routine can save images containing any type of pixel. However, the - This routine can save images containing any type of pixel. However, the DNG
DNG format can natively store only the following pixel types: rgb_pixel, format can natively store only the following pixel types: rgb_pixel,
hsi_pixel, rgb_alpha_pixel, uint8, and uint16. All other pixel types hsi_pixel, rgb_alpha_pixel, uint8, uint16, float, double, and long double.
will be converted into one of these types as appropriate before being All other pixel types will be converted into one of these types as
saved to disk. appropriate before being saved to disk.
throws throws
- image_save_error - image_save_error
This exception is thrown if there is an error that prevents us This exception is thrown if there is an error that prevents us
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment