Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save 8 bit b/w images using PseudoGrey Plus #3244

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 77 additions & 0 deletions rtengine/iimage.cc
Expand Up @@ -44,3 +44,80 @@ int rtengine::getCoarseBitMask( const procparams::CoarseTransformParams &coarse)

return tr;
}

void rtengine::ImageDatas::allocate(int W, int H)
{
}

void rtengine::ImageDatas::rotate(int deg)
{
}
void rtengine::ImageDatas::flushData()
{
allocate(0, 0);
}

void rtengine::ImageDatas::hflip()
{
}

void rtengine::ImageDatas::vflip()
{
}

void rtengine::ImageDatas::readData(FILE *fh)
{
}

void rtengine::ImageDatas::writeData(FILE *fh)
{
}

void rtengine::ImageDatas::normalizeInt(int srcMinVal, int srcMaxVal)
{
}

void rtengine::ImageDatas::normalizeFloat(float srcMinVal, float srcMaxVal)
{
}

void rtengine::ImageDatas::computeHistogramAutoWB(
double &avg_r,
double &avg_g,
double &avg_b,
int &n,
LUTu &histogram,
int compression
)
{
}

void rtengine::ImageDatas::getSpotWBData(
double &reds,
double &greens,
double &blues,
int &rn,
int &gn,
int &bn,
std::vector<Coord2D> &red,
std::vector<Coord2D> &green,
std::vector<Coord2D> &blue,
int tran
)
{
}

void rtengine::ImageDatas::getAutoWBMultipliers(double &rm, double &gm, double &bm)
{
rm = gm = bm = 1.0;
}

const char* rtengine::ImageDatas::getType() const
{
return "unknown";
}

bool rtengine::ImageDatas::isBW () const
{
return false;
}
37 changes: 15 additions & 22 deletions rtengine/iimage.h
Expand Up @@ -67,37 +67,30 @@ class ImageDatas : virtual public ImageDimensions
// parameters that will never be used, replaced by the subclasses r, g and b parameters!
// they are still necessary to implement operator() in this parent class
virtual ~ImageDatas() {}
virtual void allocate (int W, int H) {}
virtual void rotate (int deg) {}
virtual void allocate (int W, int H);
virtual void rotate (int deg);
// free the memory allocated for the image data without deleting the object.
virtual void flushData ()
{
allocate(0, 0);
}
virtual void flushData ();

virtual void hflip () {}
virtual void vflip () {}
virtual void hflip ();
virtual void vflip ();

// Read the raw dump of the data
void readData (FILE *fh) {}
void readData (FILE *fh);
// Write a raw dump of the data
void writeData (FILE *fh) {}
void writeData (FILE *fh);

virtual void normalizeInt (int srcMinVal, int srcMaxVal) {};
virtual void normalizeFloat (float srcMinVal, float srcMaxVal) {};
virtual void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, int compression) {}
virtual void normalizeInt (int srcMinVal, int srcMaxVal);
virtual void normalizeFloat (float srcMinVal, float srcMaxVal);
virtual void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, int compression);
virtual void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn,
std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue,
int tran) {}
virtual void getAutoWBMultipliers (double &rm, double &gm, double &bm)
{
rm = gm = bm = 1.0;
}
virtual const char* getType () const
{
return "unknown";
}
int tran);
virtual void getAutoWBMultipliers (double &rm, double &gm, double &bm);

virtual const char* getType () const;

virtual bool isBW () const;
};

template <>
Expand Down
32 changes: 32 additions & 0 deletions rtengine/image16.cc
Expand Up @@ -284,6 +284,38 @@ void Image16::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, Preview
#undef GCLIP
}

bool
Image16::isBW() const
{
if (!height || !width) {
return false;
}

for (int w = 0, h = height / 2; w < width; ++w) {
if (r(h, w) != g(h, w) || r(h, w) != b(h, w)) {
return false;
}
}

bool res = true;

#ifdef _OPENMP
#pragma omp parallel for reduction(&&:res)
#endif
for (int h = 0; h < height; ++h) {
for (int w = 0; w < width; ++w) {
res =
res
&& (
r(h, w) == g(h, w)
|| r(h, w) == b(h, w)
);
}
}

return res;
}

Image8*
Image16::to8()
{
Expand Down
3 changes: 3 additions & 0 deletions rtengine/image16.h
Expand Up @@ -51,6 +51,9 @@ class Image16 : public IImage16, public ImageIO
{
return sImage16;
}

virtual bool isBW () const;

virtual int getBPS ()
{
return 8 * sizeof(unsigned short);
Expand Down
94 changes: 86 additions & 8 deletions rtengine/imageio.cc
Expand Up @@ -23,6 +23,8 @@
#include <tiffio.h>
#include <cstdio>
#include <cstring>
#include <cstdint>
#include <memory>
#include <fcntl.h>
#include <libiptcdata/iptc-jpeg.h>
#include "rt_math.h"
Expand Down Expand Up @@ -61,6 +63,7 @@ FILE* g_fopen_withBinaryAndLock(const Glib::ustring& fname)
std::unique_ptr<wchar_t, GFreeFunc> wfname (reinterpret_cast<wchar_t*>(g_utf8_to_utf16 (fname.c_str (), -1, NULL, NULL, NULL)), g_free);

HANDLE hFile = CreateFileW ( wfname.get (), GENERIC_READ | GENERIC_WRITE, 0 /* no sharing allowed */, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile != INVALID_HANDLE_VALUE) {
f = _fdopen (_open_osfhandle ((intptr_t)hFile, 0), "wb");
}
Expand All @@ -83,6 +86,36 @@ Glib::ustring to_utf8 (const std::string& str)
}
}

void scanlineToPseudoGrey (const std::uint16_t *in16, unsigned char *out8, unsigned long width)
{
// For details see http://r0k.us/graphics/PseudoGreyPlus.html

static const std::uint16_t plusses[16][3] = {
{0 << 8, 0 << 8, 0 << 8},
{0 << 8, 0 << 8, 1 << 8},
{0 << 8, 0 << 8, 2 << 8},
{1 << 8, 0 << 8, 0 << 8},
{1 << 8, 0 << 8, 1 << 8},
{1 << 8, 0 << 8, 1 << 8},
{1 << 8, 0 << 8, 2 << 8},
{2 << 8, 0 << 8, 0 << 8},
{2 << 8, 0 << 8, 1 << 8},
{2 << 8, 0 << 8, 2 << 8},
{2 << 8, 0 << 8, 2 << 8},
{0 << 8, 1 << 8, 0 << 8},
{0 << 8, 1 << 8, 1 << 8},
{0 << 8, 1 << 8, 2 << 8},
{0 << 8, 1 << 8, 2 << 8},
{1 << 8, 1 << 8, 0 << 8}
};

for (unsigned long x = 0; x < width * 3; x += 3) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could probably benefit from using pointer arithmetic instead of indexing? But maybe the compiler will do that itself...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's equivalent these days and for readability I prefer indexing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, let's have the compiler take care of that one.

out8[x + 0] = adds(in16[x + 0], plusses[in16[x + 0] >> 4 & 0x0F][0]) >> 8;
out8[x + 1] = adds(in16[x + 1], plusses[in16[x + 1] >> 4 & 0x0F][1]) >> 8;
out8[x + 2] = adds(in16[x + 2], plusses[in16[x + 2] >> 4 & 0x0F][2]) >> 8;
}
}

}

Glib::ustring ImageIO::errorMsg[6] = {"Success", "Cannot read file.", "Invalid header.", "Error while reading header.", "File reading error", "Image format not supported."};
Expand Down Expand Up @@ -915,7 +948,7 @@ int ImageIO::loadPPMFromMemory(const char* buffer, int width, int height, bool s
return IMIO_SUCCESS;
}

int ImageIO::savePNG (Glib::ustring fname, int compression, volatile int bps)
int ImageIO::savePNG (Glib::ustring fname, int compression, int bps)
{

FILE *file = g_fopen_withBinaryAndLock (fname);
Expand Down Expand Up @@ -964,14 +997,26 @@ int ImageIO::savePNG (Glib::ustring fname, int compression, volatile int bps)
png_set_IHDR(png, info, width, height, bps, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_BASE);


int rowlen = width * 3 * bps / 8;
unsigned char *row = new unsigned char [rowlen];

const bool do_pseudogrey = bps == 8 && isBW();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has of course nothing to do with this change, but why the heck is bps volatile?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) There are still things left to improve in RT and you are well on to it.


const std::unique_ptr<std::uint16_t[]> row16(
do_pseudogrey
? new std::uint16_t[width * 3]
: nullptr
);

png_write_info(png, info);

for (int i = 0; i < height; i++) {
getScanline (i, row, bps);
if (do_pseudogrey) {
getScanline (i, reinterpret_cast<unsigned char*>(row16.get()), 16);
scanlineToPseudoGrey (row16.get(), row, width);
} else {
getScanline (i, row, bps);
}

if (bps == 16) {
// convert to network byte order
Expand Down Expand Up @@ -1146,6 +1191,14 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp)
int rowlen = width * 3;
unsigned char *row = new unsigned char [rowlen];

const bool do_pseudogrey = isBW();

const std::unique_ptr<std::uint16_t[]> row16(
do_pseudogrey
? new std::uint16_t[width * 3]
: nullptr
);

/* To avoid memory leaks we establish a new setjmp return context for my_error_exit to use. */
#if defined( WIN32 ) && defined( __x86_64__ )

Expand All @@ -1166,7 +1219,12 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp)

while (cinfo.next_scanline < cinfo.image_height) {

getScanline (cinfo.next_scanline, row, 8);
if (do_pseudogrey) {
getScanline (cinfo.next_scanline, reinterpret_cast<unsigned char*>(row16.get()), 16);
scanlineToPseudoGrey (row16.get(), row, width);
} else {
getScanline (cinfo.next_scanline, row, 8);
}

if (jpeg_write_scanlines (&cinfo, &row, 1) < 1) {
jpeg_destroy_compress (&cinfo);
Expand Down Expand Up @@ -1211,6 +1269,14 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed)
int lineWidth = width * 3 * bps / 8;
unsigned char* linebuffer = new unsigned char[lineWidth];

const bool do_pseudogrey = bps == 8 && isBW();

const std::unique_ptr<std::uint16_t[]> row16(
do_pseudogrey
? new std::uint16_t[width * 3]
: nullptr
);

// TODO the following needs to be looked into - do we really need two ways to write a Tiff file ?
if (exifRoot && uncompressed) {
FILE *file = g_fopen_withBinaryAndLock (fname);
Expand All @@ -1227,9 +1293,11 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed)

// buffer for the exif and iptc
int bufferSize = 165535; //TODO: Is it really 165535... or 65535 ?
if(profileData)

if(profileData) {
bufferSize += profileLength;

}

unsigned char* buffer = new unsigned char[bufferSize];
unsigned char* iptcdata = NULL;
unsigned int iptclen = 0;
Expand Down Expand Up @@ -1258,7 +1326,12 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed)
#endif

for (int i = 0; i < height; i++) {
getScanline (i, linebuffer, bps);
if (do_pseudogrey) {
getScanline (i, reinterpret_cast<unsigned char*>(row16.get()), 16);
scanlineToPseudoGrey (row16.get(), linebuffer, width);
} else {
getScanline (i, linebuffer, bps);
}

if (needsReverse)
for (int i = 0; i < lineWidth; i += 2) {
Expand Down Expand Up @@ -1370,7 +1443,12 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed)
}

for (int row = 0; row < height; row++) {
getScanline (row, linebuffer, bps);
if (do_pseudogrey) {
getScanline (row, reinterpret_cast<unsigned char*>(row16.get()), 16);
scanlineToPseudoGrey (row16.get(), linebuffer, width);
} else {
getScanline (row, linebuffer, bps);
}

if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) {
TIFFClose (out);
Expand Down
2 changes: 1 addition & 1 deletion rtengine/imageio.h
Expand Up @@ -160,7 +160,7 @@ class ImageIO : virtual public ImageDatas
int loadJPEGFromMemory (const char* buffer, int bufsize);
int loadPPMFromMemory(const char* buffer, int width, int height, bool swap, int bps);

int savePNG (Glib::ustring fname, int compression = -1, volatile int bps = -1);
int savePNG (Glib::ustring fname, int compression = -1, int bps = -1);
int saveJPEG (Glib::ustring fname, int quality = 100, int subSamp = 3);
int saveTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false);

Expand Down