4219 lines
144 KiB
Diff
4219 lines
144 KiB
Diff
--- dcraw.c 2016-11-01 01:07:55 +0000
|
|
+++ dcraw.cc 2016-11-01 14:54:02 +0000
|
|
@@ -1,3 +1,16 @@
|
|
+/*RT*/#include <glib.h>
|
|
+/*RT*/#include <glib/gstdio.h>
|
|
+/*RT*/#undef MAX
|
|
+/*RT*/#undef MIN
|
|
+/*RT*/#undef ABS
|
|
+/*RT*/#include "rt_math.h"
|
|
+/*RT*/#define NO_LCMS
|
|
+/*RT*/#define NO_JPEG
|
|
+/*RT*/#define NO_JASPER
|
|
+/*RT*/#define LOCALTIME
|
|
+/*RT*/#define DJGPP
|
|
+
|
|
+#include "opthelper.h"
|
|
/*
|
|
dcraw.c -- Dave Coffin's raw photo decoder
|
|
Copyright 1997-2016 by Dave Coffin, dcoffin a cybercom o net
|
|
@@ -29,17 +42,17 @@
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
#define _USE_MATH_DEFINES
|
|
-#include <ctype.h>
|
|
-#include <errno.h>
|
|
+#include <cctype>
|
|
+#include <cerrno>
|
|
#include <fcntl.h>
|
|
-#include <float.h>
|
|
-#include <limits.h>
|
|
-#include <math.h>
|
|
-#include <setjmp.h>
|
|
-#include <stdio.h>
|
|
-#include <stdlib.h>
|
|
-#include <string.h>
|
|
-#include <time.h>
|
|
+#include <cfloat>
|
|
+#include <climits>
|
|
+#include <cmath>
|
|
+#include <csetjmp>
|
|
+#include <cstdio>
|
|
+#include <cstdlib>
|
|
+#include <cstring>
|
|
+#include <ctime>
|
|
#include <sys/types.h>
|
|
|
|
#if defined(DJGPP) || defined(__MINGW32__)
|
|
@@ -54,7 +67,6 @@
|
|
#ifdef WIN32
|
|
#include <sys/utime.h>
|
|
#include <winsock2.h>
|
|
-#pragma comment(lib, "ws2_32.lib")
|
|
#define snprintf _snprintf
|
|
#define strcasecmp stricmp
|
|
#define strncasecmp strnicmp
|
|
@@ -89,89 +101,38 @@
|
|
#define _(String) (String)
|
|
#endif
|
|
|
|
-#if !defined(uchar)
|
|
-#define uchar unsigned char
|
|
-#endif
|
|
-#if !defined(ushort)
|
|
-#define ushort unsigned short
|
|
-#endif
|
|
+#define ushort UshORt
|
|
+typedef unsigned char uchar;
|
|
+typedef unsigned short ushort;
|
|
|
|
+#include "dcraw.h"
|
|
/*
|
|
- All global variables are defined here, and all functions that
|
|
+ RT All global variables are defined here, and all functions that
|
|
access them are prefixed with "CLASS". Note that a thread-safe
|
|
C++ class cannot have non-const static local variables.
|
|
*/
|
|
-FILE *ifp, *ofp;
|
|
-short order;
|
|
-const char *ifname;
|
|
-char *meta_data, xtrans[6][6], xtrans_abs[6][6];
|
|
-char cdesc[5], desc[512], make[64], model[64], model2[64], artist[64];
|
|
-float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len;
|
|
-time_t timestamp;
|
|
-off_t strip_offset, data_offset;
|
|
-off_t thumb_offset, meta_offset, profile_offset;
|
|
-unsigned shot_order, kodak_cbpp, exif_cfa, unique_id;
|
|
-unsigned thumb_length, meta_length, profile_length;
|
|
-unsigned thumb_misc, *oprof, fuji_layout, shot_select=0, multi_out=0;
|
|
-unsigned tiff_nifds, tiff_samples, tiff_bps, tiff_compress;
|
|
-unsigned black, maximum, mix_green, raw_color, zero_is_bad;
|
|
-unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error;
|
|
-unsigned tile_width, tile_length, gpsdata[32], load_flags;
|
|
-unsigned flip, tiff_flip, filters, colors;
|
|
-ushort raw_height, raw_width, height, width, top_margin, left_margin;
|
|
-ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height;
|
|
-ushort *raw_image, (*image)[4], cblack[4102];
|
|
-ushort white[8][8], curve[0x10000], cr2_slice[3], sraw_mul[4];
|
|
-double pixel_aspect, aber[4]={1,1,1,1}, gamm[6]={ 0.45,4.5,0,0,0,0 };
|
|
-float bright=1, user_mul[4]={0,0,0,0}, threshold=0;
|
|
-int mask[8][4];
|
|
-int half_size=0, four_color_rgb=0, document_mode=0, highlight=0;
|
|
-int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=1;
|
|
-int output_color=1, output_bps=8, output_tiff=0, med_passes=0;
|
|
-int no_auto_bright=0;
|
|
-unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX };
|
|
-float cam_mul[4], pre_mul[4], cmatrix[3][4], rgb_cam[3][4];
|
|
-const double xyz_rgb[3][3] = { /* XYZ from RGB */
|
|
+
|
|
+const double xyz_rgb[3][3] = { // XYZ from RGB
|
|
{ 0.412453, 0.357580, 0.180423 },
|
|
{ 0.212671, 0.715160, 0.072169 },
|
|
{ 0.019334, 0.119193, 0.950227 } };
|
|
const float d65_white[3] = { 0.950456, 1, 1.088754 };
|
|
-int histogram[4][0x2000];
|
|
-void (*write_thumb)(), (*write_fun)();
|
|
-void (*load_raw)(), (*thumb_load_raw)();
|
|
-jmp_buf failure;
|
|
-
|
|
-struct decode {
|
|
- struct decode *branch[2];
|
|
- int leaf;
|
|
-} first_decode[2048], *second_decode, *free_decode;
|
|
-
|
|
-struct tiff_ifd {
|
|
- int width, height, bps, comp, phint, offset, flip, samples, bytes;
|
|
- int tile_width, tile_length;
|
|
- float shutter;
|
|
-} tiff_ifd[10];
|
|
-
|
|
-struct ph1 {
|
|
- int format, key_off, tag_21a;
|
|
- int black, split_col, black_col, split_row, black_row;
|
|
- float tag_210;
|
|
-} ph1;
|
|
|
|
-#define CLASS
|
|
+/* RT: Removed unused structs */
|
|
+#define CLASS DCraw::
|
|
|
|
#define FORC(cnt) for (c=0; c < cnt; c++)
|
|
#define FORC3 FORC(3)
|
|
#define FORC4 FORC(4)
|
|
#define FORCC FORC(colors)
|
|
|
|
-#define SQR(x) ((x)*(x))
|
|
+#define SQR(x) rtengine::SQR(x)
|
|
#define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31))
|
|
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
|
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
-#define LIM(x,min,max) MAX(min,MIN(x,max))
|
|
-#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y))
|
|
-#define CLIP(x) LIM((int)(x),0,65535)
|
|
+#define MIN(a,b) rtengine::min(a,static_cast<__typeof__(a)>(b))
|
|
+#define MAX(a,b) rtengine::max(a,static_cast<__typeof__(a)>(b))
|
|
+#define LIM(x,min,max) rtengine::LIM(x,static_cast<__typeof__(x)>(min),static_cast<__typeof__(x)>(max))
|
|
+#define ULIM(x,y,z) rtengine::median(x,static_cast<__typeof__(x)>(y),static_cast<__typeof__(x)>(z))
|
|
+#define CLIP(x) rtengine::CLIP(x)
|
|
#define SWAP(a,b) { a=a+b; b=a-b; a=a-b; }
|
|
|
|
/*
|
|
@@ -247,6 +208,7 @@
|
|
|
|
if (filters == 1) return filter[(row+top_margin)&15][(col+left_margin)&15];
|
|
if (filters == 9) return xtrans[(row+6) % 6][(col+6) % 6];
|
|
+
|
|
return FC(row,col);
|
|
}
|
|
|
|
@@ -289,6 +251,7 @@
|
|
fprintf (stderr,_("Corrupt data near 0x%llx\n"), (INT64) ftello(ifp));
|
|
}
|
|
data_error++;
|
|
+/*RT Issue 2467 longjmp (failure, 1);*/
|
|
}
|
|
|
|
ushort CLASS sget2 (uchar *s)
|
|
@@ -362,7 +325,7 @@
|
|
{
|
|
if (fread (pixel, 2, count, ifp) < count) derror();
|
|
if ((order == 0x4949) == (ntohs(0x1234) == 0x1234))
|
|
- swab (pixel, pixel, count*2);
|
|
+ swab ((char*)pixel, (char*)pixel, count*2);
|
|
}
|
|
|
|
void CLASS cubic_spline (const int *x_, const int *y_, const int len)
|
|
@@ -589,13 +552,13 @@
|
|
return 0;
|
|
}
|
|
|
|
-unsigned CLASS getbithuff (int nbits, ushort *huff)
|
|
+inline unsigned CLASS getbithuff_t::operator() (int nbits, ushort *huff)
|
|
{
|
|
- static unsigned bitbuf=0;
|
|
- static int vbits=0, reset=0;
|
|
+/*RT static unsigned bitbuf=0; */
|
|
+/*RT static int vbits=0, reset=0; */
|
|
unsigned c;
|
|
|
|
- if (nbits > 25) return 0;
|
|
+ if (UNLIKELY(nbits > 25)) return 0;
|
|
if (nbits < 0)
|
|
return bitbuf = vbits = reset = 0;
|
|
if (nbits == 0 || vbits < 0) return 0;
|
|
@@ -805,9 +768,13 @@
|
|
FORC(2) free (huff[c]);
|
|
}
|
|
|
|
+/*
|
|
+ Not a full implementation of Lossless JPEG, just
|
|
+ enough to decode Canon, Kodak and Adobe DNG images.
|
|
+ */
|
|
struct jhead {
|
|
- int algo, bits, high, wide, clrs, sraw, psv, restart, vpred[6];
|
|
- ushort quant[64], idct[64], *huff[20], *free[20], *row;
|
|
+ int bits, high, wide, clrs, sraw, psv, restart, vpred[6];
|
|
+ ushort *huff[6], *free[4], *row;
|
|
};
|
|
|
|
int CLASS ljpeg_start (struct jhead *jh, int info_only)
|
|
@@ -828,9 +795,9 @@
|
|
switch (tag) {
|
|
case 0xffc3:
|
|
jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
|
|
- case 0xffc1:
|
|
+ case 0xffc1:
|
|
case 0xffc0:
|
|
- jh->algo = tag & 0xff;
|
|
+ jh->algo = tag & 0xff;
|
|
jh->bits = data[0];
|
|
jh->high = data[1] << 8 | data[2];
|
|
jh->wide = data[3] << 8 | data[4];
|
|
@@ -862,7 +829,7 @@
|
|
FORC(4) jh->huff[2+c] = jh->huff[1];
|
|
FORC(jh->sraw) jh->huff[1+c] = jh->huff[0];
|
|
}
|
|
- jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4);
|
|
+ jh->row = (ushort *) calloc (2 * jh->wide*jh->clrs, 4);
|
|
merror (jh->row, "ljpeg_start()");
|
|
return zero_after_ff = 1;
|
|
}
|
|
@@ -874,7 +841,7 @@
|
|
free (jh->row);
|
|
}
|
|
|
|
-int CLASS ljpeg_diff (ushort *huff)
|
|
+inline int CLASS ljpeg_diff (ushort *huff)
|
|
{
|
|
int len, diff;
|
|
|
|
@@ -901,7 +868,7 @@
|
|
}
|
|
getbits(-1);
|
|
}
|
|
- FORC3 row[c] = jh->row + jh->wide*jh->clrs*((jrow+c) & 1);
|
|
+ FORC3 row[c] = (jh->row + ((jrow & 1) + 1) * (jh->wide*jh->clrs*((jrow+c) & 1)));
|
|
for (col=0; col < jh->wide; col++)
|
|
FORC(jh->clrs) {
|
|
diff = ljpeg_diff (jh->huff[c]);
|
|
@@ -909,8 +876,7 @@
|
|
pred = spred;
|
|
else if (col) pred = row[0][-jh->clrs];
|
|
else pred = (jh->vpred[c] += diff) - diff;
|
|
- if (jrow && col) switch (jh->psv) {
|
|
- case 1: break;
|
|
+ if (jh->psv != 1 && jrow && col) switch (jh->psv) {
|
|
case 2: pred = row[1][0]; break;
|
|
case 3: pred = row[1][-jh->clrs]; break;
|
|
case 4: pred = pred + row[1][0] - row[1][-jh->clrs]; break;
|
|
@@ -919,7 +885,7 @@
|
|
case 7: pred = (pred + row[1][0]) >> 1; break;
|
|
default: pred = 0;
|
|
}
|
|
- if ((**row = pred + diff) >> jh->bits) derror();
|
|
+ if (UNLIKELY((**row = pred + diff) >> jh->bits)) derror();
|
|
if (c <= jh->sraw) spred = **row;
|
|
row[0]++; row[1]++;
|
|
}
|
|
@@ -928,22 +894,38 @@
|
|
|
|
void CLASS lossless_jpeg_load_raw()
|
|
{
|
|
- int jwide, jrow, jcol, val, jidx, i, j, row=0, col=0;
|
|
struct jhead jh;
|
|
- ushort *rp;
|
|
+ int row=0, col=0;
|
|
|
|
if (!ljpeg_start (&jh, 0)) return;
|
|
- jwide = jh.wide * jh.clrs;
|
|
-
|
|
- for (jrow=0; jrow < jh.high; jrow++) {
|
|
- rp = ljpeg_row (jrow, &jh);
|
|
+ int jwide = jh.wide * jh.clrs;
|
|
+ ushort *rp[2];
|
|
+ rp[0] = ljpeg_row (0, &jh);
|
|
+
|
|
+ for (int jrow=0; jrow < jh.high; jrow++) {
|
|
+#ifdef _OPENMP
|
|
+#pragma omp parallel sections
|
|
+#endif
|
|
+{
|
|
+#ifdef _OPENMP
|
|
+ #pragma omp section
|
|
+#endif
|
|
+ {
|
|
+ if(jrow < jh.high - 1)
|
|
+ rp[(jrow + 1)&1] = ljpeg_row (jrow + 1, &jh);
|
|
+ }
|
|
+#ifdef _OPENMP
|
|
+ #pragma omp section
|
|
+#endif
|
|
+ {
|
|
if (load_flags & 1)
|
|
row = jrow & 1 ? height-1-jrow/2 : jrow/2;
|
|
- for (jcol=0; jcol < jwide; jcol++) {
|
|
- val = curve[*rp++];
|
|
+ for (int jcol=0; jcol < jwide; jcol++) {
|
|
+ int val = curve[*rp[jrow&1]++];
|
|
if (cr2_slice[0]) {
|
|
- jidx = jrow*jwide + jcol;
|
|
- i = jidx / (cr2_slice[1]*raw_height);
|
|
+ int jidx = jrow*jwide + jcol;
|
|
+ int i = jidx / (cr2_slice[1]*raw_height);
|
|
+ int j;
|
|
if ((j = i >= cr2_slice[0]))
|
|
i = cr2_slice[0];
|
|
jidx -= i * (cr2_slice[1]*raw_height);
|
|
@@ -956,6 +938,8 @@
|
|
if (++col >= raw_width)
|
|
col = (row++,0);
|
|
}
|
|
+ }
|
|
+}
|
|
}
|
|
ljpeg_end (&jh);
|
|
}
|
|
@@ -1124,8 +1108,7 @@
|
|
if (++col >= tile_width || col >= raw_width)
|
|
row += 1 + (col = 0);
|
|
}
|
|
- }
|
|
- }
|
|
+ } }
|
|
fseek (ifp, save+4, SEEK_SET);
|
|
if ((tcol += tile_width) >= raw_width)
|
|
trow += tile_length + (tcol = 0);
|
|
@@ -1332,14 +1315,14 @@
|
|
int i, nz;
|
|
char tail[424];
|
|
|
|
- fseek (ifp, -sizeof tail, SEEK_END);
|
|
+ fseek (ifp, -(int)sizeof tail, SEEK_END);
|
|
fread (tail, 1, sizeof tail, ifp);
|
|
for (nz=i=0; i < sizeof tail; i++)
|
|
if (tail[i]) nz++;
|
|
return nz > 20;
|
|
}
|
|
|
|
-void CLASS jpeg_thumb();
|
|
+/*RT void CLASS jpeg_thumb(); */
|
|
|
|
void CLASS ppm_thumb()
|
|
{
|
|
@@ -1701,10 +1684,10 @@
|
|
}
|
|
}
|
|
|
|
-unsigned CLASS ph1_bithuff (int nbits, ushort *huff)
|
|
+unsigned CLASS ph1_bithuff_t::operator() (int nbits, ushort *huff)
|
|
{
|
|
- static UINT64 bitbuf=0;
|
|
- static int vbits=0;
|
|
+/*RT static UINT64 bitbuf=0; */
|
|
+/*RT static int vbits=0; */
|
|
unsigned c;
|
|
|
|
if (nbits == -1)
|
|
@@ -1779,6 +1762,338 @@
|
|
maximum = 0xfffc - ph1.black;
|
|
}
|
|
|
|
+void CLASS parse_hasselblad_gain()
|
|
+{
|
|
+ /*
|
|
+ Reverse-engineer's notes:
|
|
+
|
|
+ The Hasselblad gain tag (0x19 in makernotes) is only available in the 3FR format and
|
|
+ is applied and removed when Hasselblad Phocus converts it to the FFF format. It's
|
|
+ always 0x300000 bytes large regardless of (tested) model, not all space in it is
|
|
+ used though.
|
|
+
|
|
+ It contains individual calibration information from the factory to tune the sensor
|
|
+ performance.
|
|
+
|
|
+ There is more calibration data in the tag than what is applied in conversion to FFF,
|
|
+ I've only cared to figure out the data which is actually used, but have some leads on
|
|
+ remaining info.
|
|
+
|
|
+ The format is not equal between all models. Due to lack of 3FR files (harder to get
|
|
+ than FFF) only a subset of the models have been reverse-engineered.
|
|
+
|
|
+ The header space is 512 bytes and is a mix of 16 and 32 bit values. Offset to blocks
|
|
+ are 32 bit values, but all information seems to be encoded in 16 bit values. Many
|
|
+ values in the header can be zeroed with no effect on FFF conversion, and their
|
|
+ meaning, if any, have not been further investigated.
|
|
+
|
|
+ Formats:
|
|
+ hdr16[22] = raw width
|
|
+ hdr16[23] = raw height
|
|
+ hdr32[24] = offset to level corr block
|
|
+ Data block format. Seems to differ depending on sensor type. For tested H4D-50
|
|
+ and H3D-31: 10 16 bit signed values per row
|
|
+ value 0: a correction factor (k) used on even columns, where the new pixel value is
|
|
+ calulated as follows:
|
|
+ new_value = old_value + (2 * ((k * (old_value_on_row_above-256)) / 32767) - 2)
|
|
+ note the connection to the value on the row above, seems to be some sort of signal
|
|
+ leakage correction.
|
|
+ value 1: same as value 0 but using old value on row below instead of above
|
|
+ value 2-3: same as value 0-1 but for odd columns
|
|
+ value 4-9: has some effect if non-zero (probably similar to the others) but not
|
|
+ investigated which, as it's seems to be always zero for the tested cameras.
|
|
+
|
|
+ hdr32[25] = probably offset to "bad/unreliable pixels" info, always 512 as it starts
|
|
+ directly after the header. Not applied in FFF conversion (at least
|
|
+ typically).
|
|
+ Data block format guess: raw_height packets of
|
|
+ <type?><number of coords><coords...>
|
|
+
|
|
+ hdr32[27] = offset to unknown data (bad colulmns?), of the form:
|
|
+ <packet count><packets><0>
|
|
+ packet: <column><?><?><?>.
|
|
+
|
|
+ hdr32[34] = curves offset, seems to be A/D curves (one per channel) on newer models
|
|
+ and some sort of a film curve on older. Not applied in FFF conversion.
|
|
+
|
|
+ hdr32[36] = flatfield correction, not available in older models. Data format:
|
|
+ <1><block width><block height><rows><cols><11 * 2 pad><packets>
|
|
+ packet: <R><G1><G2><B>
|
|
+
|
|
+ The header pad is not zeroed and might seem to contain some sort of
|
|
+ information, but it makes no difference if set to zero. See
|
|
+ hasselblad_correct() how the flatfield is applied.
|
|
+
|
|
+ Applied in FFF conversion is levels, flatfield correction, and the bad columns(?)
|
|
+ data. A/D curves are surprisingly not applied, maybe pre-applied in hardware and
|
|
+ only available as information? Levels are applied before flatfield, further
|
|
+ ordering has not been investigated.
|
|
+
|
|
+ Not all combinations/models have been tested so there may be gaps.
|
|
+
|
|
+ Most clipped pixels in a 3FR is at 65535, but there's also some at 65534. Both
|
|
+ are set to 65535 when calibrated, while 65533 is treated as a normal value. In
|
|
+ the calibration process smaller values can be scaled to 65534 (which should
|
|
+ not be seen as clipped).
|
|
+ */
|
|
+
|
|
+ ushort raw_h, count, ch_count, u16;
|
|
+ int i, offset;
|
|
+ off_t base;
|
|
+
|
|
+ base = ftell(ifp);
|
|
+ fseek(ifp, 2 * 23, SEEK_CUR);
|
|
+ raw_h = get2();
|
|
+ fseek(ifp, 48, SEEK_CUR);
|
|
+ offset = get4();
|
|
+ hbd.levels = offset ? base + offset : 0;
|
|
+ fseek(ifp, 8, SEEK_CUR);
|
|
+ offset = get4();
|
|
+ hbd.unknown1 = offset ? base + offset : 0;
|
|
+ fseek(ifp, 32, SEEK_CUR);
|
|
+ offset = get4();
|
|
+ hbd.flatfield = offset ? base + offset : 0;
|
|
+}
|
|
+
|
|
+void CLASS hasselblad_correct()
|
|
+{
|
|
+ unsigned col, row;
|
|
+
|
|
+ /*
|
|
+
|
|
+ This function applies 3FR calibration data. At the time of writing it supports a
|
|
+ subset, so here's a todo list:
|
|
+
|
|
+ TODO:
|
|
+ - Support all gain tag formats
|
|
+ - The 0x19 tag varies a bit between models. We don't have parsers for all models.
|
|
+ - The reference model used was during inital reverse-engineering was a H4D-50,
|
|
+ we probably support the Hasselblads with Kodak 31, 39, 40 and 50 megapixels
|
|
+ well, but more work is needed for Kodak 16 and 22, Dalsa 60 and Sony 50.
|
|
+ - Apply bad column(?) data (hbd.unknown1)
|
|
+ - It was left out in this initial release since the effect is very small.
|
|
+ - Apply black(?) data, tag 0x1a and 0x1b is not parsed, it has however marginal
|
|
+ effect (at least for shorter exposures) so it's not too important.
|
|
+
|
|
+ While there are things to do, the current implementation supports the most
|
|
+ important aspects, the faltfield and levels calibrations applied can have strong
|
|
+ visible effects.
|
|
+
|
|
+ */
|
|
+
|
|
+ if (hbd.levels) {
|
|
+ int i;
|
|
+ fseek(ifp, hbd.levels, SEEK_SET);
|
|
+ /* skip the first set (not used as we don't apply on first/last row), we look at it though to see if
|
|
+ the levels format is one that we support (there are other formats on some models which is not
|
|
+ supported here) */
|
|
+ short test[10];
|
|
+ for (i = 0; i < 10; i++) test[i] = (short)get2();
|
|
+ if (test[5] == 0 && test[6] == 0 && test[7] == 0 && test[8] == 0 && test[9] == 0) {
|
|
+ int corr[4];
|
|
+ ushort *row_above = (ushort *)malloc(sizeof(ushort) * raw_width); // we need to cache row above as we write new values as we go
|
|
+ for (col = 0; col < raw_width; col++) row_above[col] = RAW(0,col);
|
|
+ for (row = 1; row < raw_height-1; row++) {
|
|
+ for (i = 0; i < 4; i++) corr[i] = (short)get2();
|
|
+ fseek(ifp, 6 * 2, SEEK_CUR);
|
|
+ for (col = 0; col < raw_width; col++) {
|
|
+ unsigned v = RAW(row,col);
|
|
+ if (v >= 65534) {
|
|
+ v = 65535;
|
|
+ } else {
|
|
+ if (corr[((col & 1)<<1)+0] && row_above[col] > black) v += 2 * ((corr[((col & 1)<<1)+0] * (row_above[col]-(int)black)) / 32767) - 2;
|
|
+ if (corr[((col & 1)<<1)+1] && RAW(row+1,col) > black) v += 2 * ((corr[((col & 1)<<1)+1] * (RAW(row+1,col)-(int)black)) / 32767) - 2;
|
|
+ }
|
|
+ row_above[col] = RAW(row,col);
|
|
+ RAW(row,col) = CLIP(v);
|
|
+ }
|
|
+ }
|
|
+ free(row_above);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (hbd.flatfield) {
|
|
+ int bw, bh, ffrows, ffcols, i, c;
|
|
+ ushort ref[4], ref_max;
|
|
+ fseek(ifp, hbd.flatfield, SEEK_SET);
|
|
+ get2();
|
|
+ bw = get2();
|
|
+ bh = get2();
|
|
+ ffcols = get2();
|
|
+ ffrows = get2();
|
|
+ fseek(ifp, hbd.flatfield + 16 * 2, SEEK_SET);
|
|
+
|
|
+ ushort *ffmap = (ushort *)malloc(sizeof(*ffmap) * 4 * ffcols * ffrows);
|
|
+ for (i = 0; i < 4 * ffcols * ffrows; i++) ffmap[i] = get2();
|
|
+
|
|
+ /* Get reference values from center of field. This seems to be what Phocus does too,
|
|
+ but haven't cared to figure out exactly at which coordinate */
|
|
+ i = 4 * (ffcols * ffrows / 2 + ffcols / 2);
|
|
+ ref[0] = ffmap[i+0];
|
|
+ ref[1] = ffmap[i+1];
|
|
+ ref[3] = ffmap[i+2]; // G2 = index 3 in dcraw, 2 in 3FR
|
|
+ ref[2] = ffmap[i+3];
|
|
+ ref_max = 0;
|
|
+ FORC4 if (ref[c] > ref_max) ref_max = ref[c];
|
|
+ if (ref_max == 0) ref[0] = ref[1] = ref[2] = ref[3] = ref_max = 10000;
|
|
+
|
|
+ /* Convert measured flatfield values to actual multipliers. The measured flatfield
|
|
+ can have vignetting which should be normalized, only color cast should be corrected. */
|
|
+ for (i = 0; i < 4 * ffcols * ffrows; i += 4) {
|
|
+ double base, min = 65535.0, max = 0;
|
|
+ double cur[4];
|
|
+ cur[0] = (double)ffmap[i+0] / ref[0];
|
|
+ cur[1] = (double)ffmap[i+1] / ref[1];
|
|
+ cur[3] = (double)ffmap[i+2] / ref[3]; // G2 index differs in dcraw and 3FR
|
|
+ cur[2] = (double)ffmap[i+3] / ref[2];
|
|
+ FORC4 {
|
|
+ if (cur[c] < min) min = cur[c];
|
|
+ if (cur[c] > max) max = cur[c];
|
|
+ }
|
|
+ if (max == 0) max = 1.0;
|
|
+ base = (cur[0]+cur[1]+cur[2]+cur[3])/(max*4);
|
|
+ FORC4 cur[c] = cur[c] == 0 ? 1.0 : (base * max) / cur[c];
|
|
+ /* convert to integer multiplier and store back to ffmap, we limit
|
|
+ range to 4 (16384*4) which should be fine for flatfield */
|
|
+ FORC4 {
|
|
+ cur[c] *= 16384.0;
|
|
+ if (cur[c] > 65535.0) cur[c] = 65535.0;
|
|
+ ffmap[i+c] = (ushort)cur[c];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // of the cameras we've tested we know the exact placement of the flatfield map
|
|
+ int row_offset, col_offset;
|
|
+ switch (raw_width) {
|
|
+ case 8282: // 50 megapixel Kodak
|
|
+ row_offset = 21;
|
|
+ col_offset = 71;
|
|
+ break;
|
|
+ default:
|
|
+ /* Default case for camera models we've not tested. We center the map, which may
|
|
+ not be exactly where it should be but close enough for the smooth flatfield */
|
|
+ row_offset = (raw_height - bh * ffrows) / 2;
|
|
+ col_offset = (raw_width - bw * ffcols) / 2;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ Concerning smoothing between blocks in the map Phocus makes it only vertically,
|
|
+ probably because it's simpler and faster. Looking at actual flatfield data it seems
|
|
+ like it's better to smooth in both directions. Possibly flatfield could be used for
|
|
+ correcting tiling on Dalsa sensors (H4D-60) like partly done in Phase One IIQ format,
|
|
+ and then sharp edges may be beneficial at least at the tiling seams, but at the time
|
|
+ of writing I've had no H4D-60 3FR files to test with to verify that.
|
|
+
|
|
+ Meanwhile we do both vertical and horizontal smoothing/blurring.
|
|
+ */
|
|
+
|
|
+ /* pre-calculate constants for blurring. We probably should make a more efficient
|
|
+ blur than this, but this does not need any buffer and makes nice-looking
|
|
+ radial gradients */
|
|
+ ushort *corners_weight = (ushort *)malloc(bw*bh*9*sizeof(*corners_weight));
|
|
+ const int corners_mix[9][4][2] = { { {0,0}, {0,1}, {1,0}, {1,1} },
|
|
+ { {0,1}, {1,1}, {-1,-1}, {-1,-1} },
|
|
+ { {0,1}, {0,2}, {1,1}, {1,2} },
|
|
+ { {1,0}, {1,1}, {-1,-1}, {-1,-1} },
|
|
+ { {1,1}, {-1,-1}, {-1,-1}, {-1,-1} },
|
|
+ { {1,1}, {1,2}, {-1,-1}, {-1,-1} },
|
|
+ { {1,0}, {1,1}, {2,0}, {2,1} },
|
|
+ { {1,1}, {2,1}, {-1,-1}, {-1,-1} },
|
|
+ { {1,1}, {1,2}, {2,1}, {2,2} } };
|
|
+ const ushort corners_shift[9] = { 2, 1, 2, 1, 0, 1, 2, 1, 2 };
|
|
+ for (row = 0; row < bh; row++) {
|
|
+ const ushort maxdist = bw < bh ? bw/2-1 : bh/2-1;
|
|
+ const unsigned bwu = (unsigned)bw;
|
|
+ const unsigned bhu = (unsigned)bh;
|
|
+ const unsigned corners[9][2] = {{0,0}, {0,bwu/2}, {0,bwu-1},
|
|
+ {bhu/2,0},{bhu/2,bwu/2},{bhu/2,bwu-1},
|
|
+ {bhu-1,0},{bhu-1,bwu/2},{bhu-1,bwu-1}};
|
|
+ for (col = 0; col < bw; col++) {
|
|
+ for (i = 0; i < 9; i++) {
|
|
+ ushort dist = (ushort)sqrt(abs(corners[i][0] - row) * abs(corners[i][0] - row) + abs(corners[i][1] - col) * abs(corners[i][1] - col));
|
|
+ ushort weight = dist > maxdist ? 0 : maxdist - dist;
|
|
+ corners_weight[9*(row*bw+col)+i] = weight;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // apply flatfield
|
|
+#pragma omp parallel for
|
|
+ for (int row = 0; row < raw_height; row++) {
|
|
+ int ffs, cur_ffr, i, c;
|
|
+ if (row < row_offset) {
|
|
+ cur_ffr = row_offset;
|
|
+ ffs = 0;
|
|
+ } else if (row >= row_offset + ffrows * bh) {
|
|
+ cur_ffr = row_offset + (ffrows-1) * bh;
|
|
+ ffs = 4 * ffcols * (ffrows-1);
|
|
+ } else {
|
|
+ cur_ffr = row_offset + bh * ((row - row_offset) / bh);
|
|
+ ffs = 4 * ffcols * ((row - row_offset) / bh);
|
|
+ }
|
|
+ int next_ffc = 0, cur_ffc = col_offset;
|
|
+ int ffc = ffs;
|
|
+ ushort *cur[3][3]; // points to local ffmap entries with center at cur[1][1]
|
|
+ for (int col = 0; col < raw_width; col++) {
|
|
+ if (col == next_ffc) {
|
|
+ int rowsub = ffs == 0 ? 0 : ffcols*4;
|
|
+ int rowadd = ffs == 4 * ffcols * (ffrows-1) ? 0 : ffcols * 4;
|
|
+ int colsub = ffc == ffs ? 0 : 4;
|
|
+ int coladd = ffc == ffs + 4 * (ffcols-1) ? 0 : 4;
|
|
+ if (col != 0) cur_ffc = next_ffc;
|
|
+ else next_ffc += col_offset;
|
|
+ next_ffc += bw;
|
|
+
|
|
+ cur[0][0] = &ffmap[ffc-rowsub-colsub];
|
|
+ cur[0][1] = &ffmap[ffc-rowsub];
|
|
+ cur[0][2] = &ffmap[ffc-rowsub+coladd];
|
|
+
|
|
+ cur[1][0] = &ffmap[ffc-colsub];
|
|
+ cur[1][1] = &ffmap[ffc];
|
|
+ cur[1][2] = &ffmap[ffc+coladd];
|
|
+
|
|
+ cur[2][0] = &ffmap[ffc+rowadd-colsub];
|
|
+ cur[2][1] = &ffmap[ffc+rowadd];
|
|
+ cur[2][2] = &ffmap[ffc+rowadd+coladd];
|
|
+
|
|
+ ffc += 4;
|
|
+ if (ffc == ffs + 4 * ffcols) next_ffc += raw_width; // last col in map, avoid stepping further
|
|
+ }
|
|
+ unsigned v = RAW(row,col);
|
|
+ if (v > black && v < 65535) {
|
|
+ c = FC(row,col);
|
|
+ unsigned x = col < cur_ffc ? 0 : col - cur_ffc;
|
|
+ unsigned y = row < cur_ffr ? 0 : row - cur_ffr;
|
|
+ if (x >= bw) x = bw-1;
|
|
+ if (y >= bh) y = bh-1;
|
|
+ unsigned wsum = 0;
|
|
+ unsigned mul = 0;
|
|
+ for (i = 0; i < 9; i++) {
|
|
+ ushort cw = corners_weight[9*(y*bw+x)+i];
|
|
+ if (cw) {
|
|
+ unsigned m = 0;
|
|
+ int j;
|
|
+ for (j = 0; j < 1 << corners_shift[i]; j++) {
|
|
+ int cr = corners_mix[i][j][0], cc = corners_mix[i][j][1];
|
|
+ m += cur[cr][cc][c];
|
|
+ }
|
|
+ m >>= corners_shift[i];
|
|
+ mul += m * cw;
|
|
+ wsum += cw;
|
|
+ }
|
|
+ }
|
|
+ mul /= wsum;
|
|
+ v = black + ((v-black) * mul) / 16384;
|
|
+ RAW(row,col) = v > 65535 ? 65535 : v;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ free(ffmap);
|
|
+ free(corners_weight);
|
|
+ }
|
|
+}
|
|
+
|
|
void CLASS hasselblad_load_raw()
|
|
{
|
|
struct jhead jh;
|
|
@@ -2002,10 +2317,10 @@
|
|
maximum = curve[0x3ff];
|
|
}
|
|
|
|
-unsigned CLASS pana_bits (int nbits)
|
|
+unsigned CLASS pana_bits_t::operator() (int nbits)
|
|
{
|
|
- static uchar buf[0x4000];
|
|
- static int vbits;
|
|
+/*RT static uchar buf[0x4000]; */
|
|
+/*RT static int vbits;*/
|
|
int byte;
|
|
|
|
if (!nbits) return vbits=0;
|
|
@@ -2188,7 +2503,7 @@
|
|
|
|
void CLASS kodak_radc_load_raw()
|
|
{
|
|
- static const char src[] = {
|
|
+ static const signed char src[] = {
|
|
1,1, 2,3, 3,4, 4,2, 5,7, 6,5, 7,6, 7,8,
|
|
1,0, 2,1, 3,3, 4,4, 5,2, 6,7, 7,6, 8,5, 8,8,
|
|
2,1, 2,3, 3,0, 3,2, 3,4, 4,6, 5,5, 6,7, 6,8,
|
|
@@ -2294,11 +2609,11 @@
|
|
METHODDEF(boolean)
|
|
fill_input_buffer (j_decompress_ptr cinfo)
|
|
{
|
|
- static uchar jpeg_buffer[4096];
|
|
+/*RT static uchar jpeg_buffer[4096]; */
|
|
size_t nbytes;
|
|
|
|
nbytes = fread (jpeg_buffer, 1, 4096, ifp);
|
|
- swab (jpeg_buffer, jpeg_buffer, nbytes);
|
|
+ swab ((char*)jpeg_buffer, (char*)jpeg_buffer, nbytes);
|
|
cinfo->src->next_input_byte = jpeg_buffer;
|
|
cinfo->src->bytes_in_buffer = nbytes;
|
|
return TRUE;
|
|
@@ -2648,10 +2963,9 @@
|
|
maximum = (1 << (thumb_misc & 31)) - 1;
|
|
}
|
|
|
|
-void CLASS sony_decrypt (unsigned *data, int len, int start, int key)
|
|
+void CLASS sony_decrypt_t::operator()(unsigned *data, int len, int start, int key)
|
|
{
|
|
- static unsigned pad[128], p;
|
|
-
|
|
+/*RT static unsigned pad[128], p;*/
|
|
if (start) {
|
|
for (p=0; p < 4; p++)
|
|
pad[p] = key = key * 48828125 + 1;
|
|
@@ -2736,11 +3050,13 @@
|
|
bit += 7;
|
|
}
|
|
for (i=0; i < 16; i++, col+=2)
|
|
- RAW(row,col) = curve[pix[i] << 1] >> 2;
|
|
+ RAW(row,col) = curve[pix[i] << 1]; // >> 2; RT: disabled shifting to avoid precision loss
|
|
col -= col & 1 ? 1:31;
|
|
}
|
|
}
|
|
free (data);
|
|
+ maximum = curve[0x7ff << 1]; // RT: fix maximum.
|
|
+ maximum = 16300; // RT: conservative white level tested on various ARW2 cameras. This constant was set in 2013-12-17, may need re-evaluation in the future.
|
|
}
|
|
|
|
void CLASS samsung_load_raw()
|
|
@@ -2905,7 +3221,10 @@
|
|
diff = diff ? -diff : 0x80;
|
|
if (ftell(ifp) + 12 >= seg[1][1])
|
|
diff = 0;
|
|
- raw_image[pix] = pred[pix & 1] += diff;
|
|
+ if(pix>=raw_width*raw_height)
|
|
+ derror();
|
|
+ else
|
|
+ raw_image[pix] = pred[pix & 1] += diff;
|
|
if (!(pix & 1) && HOLE(pix / raw_width)) pix += 2;
|
|
}
|
|
maximum = 0xff;
|
|
@@ -3038,7 +3357,7 @@
|
|
|
|
void CLASS foveon_decoder (unsigned size, unsigned code)
|
|
{
|
|
- static unsigned huff[1024];
|
|
+/*RT static unsigned huff[1024];*/
|
|
struct decode *cur;
|
|
int i, len;
|
|
|
|
@@ -3135,7 +3454,7 @@
|
|
pred[c] += diff[dindex->leaf];
|
|
if (pred[c] >> 16 && ~pred[c] >> 16) derror();
|
|
}
|
|
- FORC3 image[row*width+col][c] = pred[c];
|
|
+ FORC3 image[row*width+col][c] = pred[c] < 0 ? 0 : pred[c];
|
|
}
|
|
}
|
|
}
|
|
@@ -3746,6 +4065,8 @@
|
|
if (load_raw == &CLASS phase_one_load_raw ||
|
|
load_raw == &CLASS phase_one_load_raw_c)
|
|
phase_one_correct();
|
|
+ if (load_raw == &CLASS hasselblad_load_raw) // RT
|
|
+ hasselblad_correct(); // RT
|
|
if (fuji_width) {
|
|
for (row=0; row < raw_height-top_margin*2; row++) {
|
|
for (col=0; col < fuji_width << !fuji_layout; col++) {
|
|
@@ -3761,10 +4082,13 @@
|
|
}
|
|
}
|
|
} else {
|
|
- for (row=0; row < height; row++)
|
|
- for (col=0; col < width; col++)
|
|
+
|
|
+#pragma omp parallel for
|
|
+ for (int row=0; row < height; row++)
|
|
+ for (int col=0; col < width; col++)
|
|
BAYER2(row,col) = RAW(row+top_margin,col+left_margin);
|
|
}
|
|
+
|
|
if (mask[0][3] > 0) goto mask_set;
|
|
if (load_raw == &CLASS canon_load_raw ||
|
|
load_raw == &CLASS lossless_jpeg_load_raw) {
|
|
@@ -3808,127 +4132,127 @@
|
|
}
|
|
}
|
|
|
|
-void CLASS remove_zeroes()
|
|
-{
|
|
- unsigned row, col, tot, n, r, c;
|
|
-
|
|
- for (row=0; row < height; row++)
|
|
- for (col=0; col < width; col++)
|
|
- if (BAYER(row,col) == 0) {
|
|
- tot = n = 0;
|
|
- for (r = row-2; r <= row+2; r++)
|
|
- for (c = col-2; c <= col+2; c++)
|
|
- if (r < height && c < width &&
|
|
- FC(r,c) == FC(row,col) && BAYER(r,c))
|
|
- tot += (n++,BAYER(r,c));
|
|
- if (n) BAYER(row,col) = tot/n;
|
|
- }
|
|
-}
|
|
+//void CLASS remove_zeroes()
|
|
+//{
|
|
+// unsigned row, col, tot, n, r, c;
|
|
+//
|
|
+// for (row=0; row < height; row++)
|
|
+// for (col=0; col < width; col++)
|
|
+// if (BAYER(row,col) == 0) {
|
|
+// tot = n = 0;
|
|
+// for (r = row-2; r <= row+2; r++)
|
|
+// for (c = col-2; c <= col+2; c++)
|
|
+// if (r < height && c < width &&
|
|
+// FC(r,c) == FC(row,col) && BAYER(r,c))
|
|
+// tot += (n++,BAYER(r,c));
|
|
+// if (n) BAYER(row,col) = tot/n;
|
|
+// }
|
|
+//}
|
|
|
|
/*
|
|
Seach from the current directory up to the root looking for
|
|
a ".badpixels" file, and fix those pixels now.
|
|
*/
|
|
-void CLASS bad_pixels (const char *cfname)
|
|
-{
|
|
- FILE *fp=0;
|
|
- char *fname, *cp, line[128];
|
|
- int len, time, row, col, r, c, rad, tot, n, fixed=0;
|
|
-
|
|
- if (!filters) return;
|
|
- if (cfname)
|
|
- fp = fopen (cfname, "r");
|
|
- else {
|
|
- for (len=32 ; ; len *= 2) {
|
|
- fname = (char *) malloc (len);
|
|
- if (!fname) return;
|
|
- if (getcwd (fname, len-16)) break;
|
|
- free (fname);
|
|
- if (errno != ERANGE) return;
|
|
- }
|
|
-#if defined(WIN32) || defined(DJGPP)
|
|
- if (fname[1] == ':')
|
|
- memmove (fname, fname+2, len-2);
|
|
- for (cp=fname; *cp; cp++)
|
|
- if (*cp == '\\') *cp = '/';
|
|
-#endif
|
|
- cp = fname + strlen(fname);
|
|
- if (cp[-1] == '/') cp--;
|
|
- while (*fname == '/') {
|
|
- strcpy (cp, "/.badpixels");
|
|
- if ((fp = fopen (fname, "r"))) break;
|
|
- if (cp == fname) break;
|
|
- while (*--cp != '/');
|
|
- }
|
|
- free (fname);
|
|
- }
|
|
- if (!fp) return;
|
|
- while (fgets (line, 128, fp)) {
|
|
- cp = strchr (line, '#');
|
|
- if (cp) *cp = 0;
|
|
- if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue;
|
|
- if ((unsigned) col >= width || (unsigned) row >= height) continue;
|
|
- if (time > timestamp) continue;
|
|
- for (tot=n=0, rad=1; rad < 3 && n==0; rad++)
|
|
- for (r = row-rad; r <= row+rad; r++)
|
|
- for (c = col-rad; c <= col+rad; c++)
|
|
- if ((unsigned) r < height && (unsigned) c < width &&
|
|
- (r != row || c != col) && fcol(r,c) == fcol(row,col)) {
|
|
- tot += BAYER2(r,c);
|
|
- n++;
|
|
- }
|
|
- BAYER2(row,col) = tot/n;
|
|
- if (verbose) {
|
|
- if (!fixed++)
|
|
- fprintf (stderr,_("Fixed dead pixels at:"));
|
|
- fprintf (stderr, " %d,%d", col, row);
|
|
- }
|
|
- }
|
|
- if (fixed) fputc ('\n', stderr);
|
|
- fclose (fp);
|
|
-}
|
|
-
|
|
-void CLASS subtract (const char *fname)
|
|
-{
|
|
- FILE *fp;
|
|
- int dim[3]={0,0,0}, comment=0, number=0, error=0, nd=0, c, row, col;
|
|
- ushort *pixel;
|
|
-
|
|
- if (!(fp = fopen (fname, "rb"))) {
|
|
- perror (fname); return;
|
|
- }
|
|
- if (fgetc(fp) != 'P' || fgetc(fp) != '5') error = 1;
|
|
- while (!error && nd < 3 && (c = fgetc(fp)) != EOF) {
|
|
- if (c == '#') comment = 1;
|
|
- if (c == '\n') comment = 0;
|
|
- if (comment) continue;
|
|
- if (isdigit(c)) number = 1;
|
|
- if (number) {
|
|
- if (isdigit(c)) dim[nd] = dim[nd]*10 + c -'0';
|
|
- else if (isspace(c)) {
|
|
- number = 0; nd++;
|
|
- } else error = 1;
|
|
- }
|
|
- }
|
|
- if (error || nd < 3) {
|
|
- fprintf (stderr,_("%s is not a valid PGM file!\n"), fname);
|
|
- fclose (fp); return;
|
|
- } else if (dim[0] != width || dim[1] != height || dim[2] != 65535) {
|
|
- fprintf (stderr,_("%s has the wrong dimensions!\n"), fname);
|
|
- fclose (fp); return;
|
|
- }
|
|
- pixel = (ushort *) calloc (width, sizeof *pixel);
|
|
- merror (pixel, "subtract()");
|
|
- for (row=0; row < height; row++) {
|
|
- fread (pixel, 2, width, fp);
|
|
- for (col=0; col < width; col++)
|
|
- BAYER(row,col) = MAX (BAYER(row,col) - ntohs(pixel[col]), 0);
|
|
- }
|
|
- free (pixel);
|
|
- fclose (fp);
|
|
- memset (cblack, 0, sizeof cblack);
|
|
- black = 0;
|
|
-}
|
|
+//void CLASS bad_pixels (const char *cfname)
|
|
+//{
|
|
+// FILE *fp=0;
|
|
+// char *fname, *cp, line[128];
|
|
+// int len, time, row, col, r, c, rad, tot, n, fixed=0;
|
|
+//
|
|
+// if (!filters) return;
|
|
+// if (cfname)
|
|
+// fp = fopen (cfname, "r");
|
|
+// else {
|
|
+// for (len=32 ; ; len *= 2) {
|
|
+// fname = (char *) malloc (len);
|
|
+// if (!fname) return;
|
|
+// if (getcwd (fname, len-16)) break;
|
|
+// free (fname);
|
|
+// if (errno != ERANGE) return;
|
|
+// }
|
|
+//#if defined(WIN32) || defined(DJGPP)
|
|
+// if (fname[1] == ':')
|
|
+// memmove (fname, fname+2, len-2);
|
|
+// for (cp=fname; *cp; cp++)
|
|
+// if (*cp == '\\') *cp = '/';
|
|
+//#endif
|
|
+// cp = fname + strlen(fname);
|
|
+// if (cp[-1] == '/') cp--;
|
|
+// while (*fname == '/') {
|
|
+// strcpy (cp, "/.badpixels");
|
|
+// if ((fp = fopen (fname, "r"))) break;
|
|
+// if (cp == fname) break;
|
|
+// while (*--cp != '/');
|
|
+// }
|
|
+// free (fname);
|
|
+// }
|
|
+// if (!fp) return;
|
|
+// while (fgets (line, 128, fp)) {
|
|
+// cp = strchr (line, '#');
|
|
+// if (cp) *cp = 0;
|
|
+// if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue;
|
|
+// if ((unsigned) col >= width || (unsigned) row >= height) continue;
|
|
+// if (time > timestamp) continue;
|
|
+// for (tot=n=0, rad=1; rad < 3 && n==0; rad++)
|
|
+// for (r = row-rad; r <= row+rad; r++)
|
|
+// for (c = col-rad; c <= col+rad; c++)
|
|
+// if ((unsigned) r < height && (unsigned) c < width &&
|
|
+// (r != row || c != col) && fcol(r,c) == fcol(row,col)) {
|
|
+// tot += BAYER2(r,c);
|
|
+// n++;
|
|
+// }
|
|
+// BAYER2(row,col) = tot/n;
|
|
+// if (verbose) {
|
|
+// if (!fixed++)
|
|
+// fprintf (stderr,_("Fixed dead pixels at:"));
|
|
+// fprintf (stderr, " %d,%d", col, row);
|
|
+// }
|
|
+// }
|
|
+// if (fixed) fputc ('\n', stderr);
|
|
+// fclose (fp);
|
|
+//}
|
|
+
|
|
+//void CLASS subtract (const char *fname)
|
|
+//{
|
|
+// FILE *fp;
|
|
+// int dim[3]={0,0,0}, comment=0, number=0, error=0, nd=0, c, row, col;
|
|
+// ushort *pixel;
|
|
+//
|
|
+// if (!(fp = fopen (fname, "rb"))) {
|
|
+// perror (fname); return;
|
|
+// }
|
|
+// if (fgetc(fp) != 'P' || fgetc(fp) != '5') error = 1;
|
|
+// while (!error && nd < 3 && (c = fgetc(fp)) != EOF) {
|
|
+// if (c == '#') comment = 1;
|
|
+// if (c == '\n') comment = 0;
|
|
+// if (comment) continue;
|
|
+// if (isdigit(c)) number = 1;
|
|
+// if (number) {
|
|
+// if (isdigit(c)) dim[nd] = dim[nd]*10 + c -'0';
|
|
+// else if (isspace(c)) {
|
|
+// number = 0; nd++;
|
|
+// } else error = 1;
|
|
+// }
|
|
+// }
|
|
+// if (error || nd < 3) {
|
|
+// fprintf (stderr,_("%s is not a valid PGM file!\n"), fname);
|
|
+// fclose (fp); return;
|
|
+// } else if (dim[0] != width || dim[1] != height || dim[2] != 65535) {
|
|
+// fprintf (stderr,_("%s has the wrong dimensions!\n"), fname);
|
|
+// fclose (fp); return;
|
|
+// }
|
|
+// pixel = (ushort *) calloc (width, sizeof *pixel);
|
|
+// merror (pixel, "subtract()");
|
|
+// for (row=0; row < height; row++) {
|
|
+// fread (pixel, 2, width, fp);
|
|
+// for (col=0; col < width; col++)
|
|
+// BAYER(row,col) = MAX (BAYER(row,col) - ntohs(pixel[col]), 0);
|
|
+// }
|
|
+// free (pixel);
|
|
+// fclose (fp);
|
|
+// memset (cblack, 0, sizeof cblack);
|
|
+// black = 0;
|
|
+//}
|
|
|
|
void CLASS gamma_curve (double pwr, double ts, int mode, int imax)
|
|
{
|
|
@@ -4093,94 +4417,94 @@
|
|
}
|
|
#endif
|
|
|
|
-void CLASS hat_transform (float *temp, float *base, int st, int size, int sc)
|
|
-{
|
|
- int i;
|
|
- for (i=0; i < sc; i++)
|
|
- temp[i] = 2*base[st*i] + base[st*(sc-i)] + base[st*(i+sc)];
|
|
- for (; i+sc < size; i++)
|
|
- temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(i+sc)];
|
|
- for (; i < size; i++)
|
|
- temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(2*size-2-(i+sc))];
|
|
-}
|
|
-
|
|
-void CLASS wavelet_denoise()
|
|
-{
|
|
- float *fimg=0, *temp, thold, mul[2], avg, diff;
|
|
- int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
|
|
- ushort *window[4];
|
|
- static const float noise[] =
|
|
- { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 };
|
|
-
|
|
- if (verbose) fprintf (stderr,_("Wavelet denoising...\n"));
|
|
-
|
|
- while (maximum << scale < 0x10000) scale++;
|
|
- maximum <<= --scale;
|
|
- black <<= scale;
|
|
- FORC4 cblack[c] <<= scale;
|
|
- if ((size = iheight*iwidth) < 0x15550000)
|
|
- fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg);
|
|
- merror (fimg, "wavelet_denoise()");
|
|
- temp = fimg + size*3;
|
|
- if ((nc = colors) == 3 && filters) nc++;
|
|
- FORC(nc) { /* denoise R,G1,B,G3 individually */
|
|
- for (i=0; i < size; i++)
|
|
- fimg[i] = 256 * sqrt(image[i][c] << scale);
|
|
- for (hpass=lev=0; lev < 5; lev++) {
|
|
- lpass = size*((lev & 1)+1);
|
|
- for (row=0; row < iheight; row++) {
|
|
- hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev);
|
|
- for (col=0; col < iwidth; col++)
|
|
- fimg[lpass + row*iwidth + col] = temp[col] * 0.25;
|
|
- }
|
|
- for (col=0; col < iwidth; col++) {
|
|
- hat_transform (temp, fimg+lpass+col, iwidth, iheight, 1 << lev);
|
|
- for (row=0; row < iheight; row++)
|
|
- fimg[lpass + row*iwidth + col] = temp[row] * 0.25;
|
|
- }
|
|
- thold = threshold * noise[lev];
|
|
- for (i=0; i < size; i++) {
|
|
- fimg[hpass+i] -= fimg[lpass+i];
|
|
- if (fimg[hpass+i] < -thold) fimg[hpass+i] += thold;
|
|
- else if (fimg[hpass+i] > thold) fimg[hpass+i] -= thold;
|
|
- else fimg[hpass+i] = 0;
|
|
- if (hpass) fimg[i] += fimg[hpass+i];
|
|
- }
|
|
- hpass = lpass;
|
|
- }
|
|
- for (i=0; i < size; i++)
|
|
- image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000);
|
|
- }
|
|
- if (filters && colors == 3) { /* pull G1 and G3 closer together */
|
|
- for (row=0; row < 2; row++) {
|
|
- mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1];
|
|
- blk[row] = cblack[FC(row,0) | 1];
|
|
- }
|
|
- for (i=0; i < 4; i++)
|
|
- window[i] = (ushort *) fimg + width*i;
|
|
- for (wlast=-1, row=1; row < height-1; row++) {
|
|
- while (wlast < row+1) {
|
|
- for (wlast++, i=0; i < 4; i++)
|
|
- window[(i+3) & 3] = window[i];
|
|
- for (col = FC(wlast,1) & 1; col < width; col+=2)
|
|
- window[2][col] = BAYER(wlast,col);
|
|
- }
|
|
- thold = threshold/512;
|
|
- for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) {
|
|
- avg = ( window[0][col-1] + window[0][col+1] +
|
|
- window[2][col-1] + window[2][col+1] - blk[~row & 1]*4 )
|
|
- * mul[row & 1] + (window[1][col] + blk[row & 1]) * 0.5;
|
|
- avg = avg < 0 ? 0 : sqrt(avg);
|
|
- diff = sqrt(BAYER(row,col)) - avg;
|
|
- if (diff < -thold) diff += thold;
|
|
- else if (diff > thold) diff -= thold;
|
|
- else diff = 0;
|
|
- BAYER(row,col) = CLIP(SQR(avg+diff) + 0.5);
|
|
- }
|
|
- }
|
|
- }
|
|
- free (fimg);
|
|
-}
|
|
+//void CLASS hat_transform (float *temp, float *base, int st, int size, int sc)
|
|
+//{
|
|
+// int i;
|
|
+// for (i=0; i < sc; i++)
|
|
+// temp[i] = 2*base[st*i] + base[st*(sc-i)] + base[st*(i+sc)];
|
|
+// for (; i+sc < size; i++)
|
|
+// temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(i+sc)];
|
|
+// for (; i < size; i++)
|
|
+// temp[i] = 2*base[st*i] + base[st*(i-sc)] + base[st*(2*size-2-(i+sc))];
|
|
+//}
|
|
+
|
|
+//void CLASS wavelet_denoise()
|
|
+//{
|
|
+// float *fimg=0, *temp, thold, mul[2], avg, diff;
|
|
+// int scale=1, size, lev, hpass, lpass, row, col, nc, c, i, wlast, blk[2];
|
|
+// ushort *window[4];
|
|
+// static const float noise[] =
|
|
+// { 0.8002,0.2735,0.1202,0.0585,0.0291,0.0152,0.0080,0.0044 };
|
|
+//
|
|
+// if (verbose) fprintf (stderr,_("Wavelet denoising...\n"));
|
|
+//
|
|
+// while (maximum << scale < 0x10000) scale++;
|
|
+// maximum <<= --scale;
|
|
+// black <<= scale;
|
|
+// FORC4 cblack[c] <<= scale;
|
|
+// if ((size = iheight*iwidth) < 0x15550000)
|
|
+// fimg = (float *) malloc ((size*3 + iheight + iwidth) * sizeof *fimg);
|
|
+// merror (fimg, "wavelet_denoise()");
|
|
+// temp = fimg + size*3;
|
|
+// if ((nc = colors) == 3 && filters) nc++;
|
|
+// FORC(nc) { /* denoise R,G1,B,G3 individually */
|
|
+// for (i=0; i < size; i++)
|
|
+// fimg[i] = 256 * sqrt(image[i][c] << scale);
|
|
+// for (hpass=lev=0; lev < 5; lev++) {
|
|
+// lpass = size*((lev & 1)+1);
|
|
+// for (row=0; row < iheight; row++) {
|
|
+// hat_transform (temp, fimg+hpass+row*iwidth, 1, iwidth, 1 << lev);
|
|
+// for (col=0; col < iwidth; col++)
|
|
+// fimg[lpass + row*iwidth + col] = temp[col] * 0.25;
|
|
+// }
|
|
+// for (col=0; col < iwidth; col++) {
|
|
+// hat_transform (temp, fimg+lpass+col, iwidth, iheight, 1 << lev);
|
|
+// for (row=0; row < iheight; row++)
|
|
+// fimg[lpass + row*iwidth + col] = temp[row] * 0.25;
|
|
+// }
|
|
+// thold = threshold * noise[lev];
|
|
+// for (i=0; i < size; i++) {
|
|
+// fimg[hpass+i] -= fimg[lpass+i];
|
|
+// if (fimg[hpass+i] < -thold) fimg[hpass+i] += thold;
|
|
+// else if (fimg[hpass+i] > thold) fimg[hpass+i] -= thold;
|
|
+// else fimg[hpass+i] = 0;
|
|
+// if (hpass) fimg[i] += fimg[hpass+i];
|
|
+// }
|
|
+// hpass = lpass;
|
|
+// }
|
|
+// for (i=0; i < size; i++)
|
|
+// image[i][c] = CLIP(SQR(fimg[i]+fimg[lpass+i])/0x10000);
|
|
+// }
|
|
+// if (filters && colors == 3) { /* pull G1 and G3 closer together */
|
|
+// for (row=0; row < 2; row++) {
|
|
+// mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1];
|
|
+// blk[row] = cblack[FC(row,0) | 1];
|
|
+// }
|
|
+// for (i=0; i < 4; i++)
|
|
+// window[i] = (ushort *) fimg + width*i;
|
|
+// for (wlast=-1, row=1; row < height-1; row++) {
|
|
+// while (wlast < row+1) {
|
|
+// for (wlast++, i=0; i < 4; i++)
|
|
+// window[(i+3) & 3] = window[i];
|
|
+// for (col = FC(wlast,1) & 1; col < width; col+=2)
|
|
+// window[2][col] = BAYER(wlast,col);
|
|
+// }
|
|
+// thold = threshold/512;
|
|
+// for (col = (FC(row,0) & 1)+1; col < width-1; col+=2) {
|
|
+// avg = ( window[0][col-1] + window[0][col+1] +
|
|
+// window[2][col-1] + window[2][col+1] - blk[~row & 1]*4 )
|
|
+// * mul[row & 1] + (window[1][col] + blk[row & 1]) * 0.5;
|
|
+// avg = avg < 0 ? 0 : sqrt(avg);
|
|
+// diff = sqrt(BAYER(row,col)) - avg;
|
|
+// if (diff < -thold) diff += thold;
|
|
+// else if (diff > thold) diff -= thold;
|
|
+// else diff = 0;
|
|
+// BAYER(row,col) = CLIP(SQR(avg+diff) + 0.5);
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+// free (fimg);
|
|
+//}
|
|
|
|
void CLASS scale_colors()
|
|
{
|
|
@@ -4238,7 +4562,7 @@
|
|
if (pre_mul[3] == 0) pre_mul[3] = colors < 4 ? pre_mul[1] : 1;
|
|
dark = black;
|
|
sat = maximum;
|
|
- if (threshold) wavelet_denoise();
|
|
+// if (threshold) wavelet_denoise();
|
|
maximum -= black;
|
|
for (dmin=DBL_MAX, dmax=c=0; c < 4; c++) {
|
|
if (dmin > pre_mul[c])
|
|
@@ -4344,776 +4668,440 @@
|
|
if (half_size) filters = 0;
|
|
}
|
|
|
|
-void CLASS border_interpolate (int border)
|
|
-{
|
|
- unsigned row, col, y, x, f, c, sum[8];
|
|
-
|
|
- for (row=0; row < height; row++)
|
|
- for (col=0; col < width; col++) {
|
|
- if (col==border && row >= border && row < height-border)
|
|
- col = width-border;
|
|
- memset (sum, 0, sizeof sum);
|
|
- for (y=row-1; y != row+2; y++)
|
|
- for (x=col-1; x != col+2; x++)
|
|
- if (y < height && x < width) {
|
|
- f = fcol(y,x);
|
|
- sum[f] += image[y*width+x][f];
|
|
- sum[f+4]++;
|
|
- }
|
|
- f = fcol(row,col);
|
|
- FORCC if (c != f && sum[c+4])
|
|
- image[row*width+col][c] = sum[c] / sum[c+4];
|
|
- }
|
|
-}
|
|
-
|
|
-void CLASS lin_interpolate()
|
|
-{
|
|
- int code[16][16][32], size=16, *ip, sum[4];
|
|
- int f, c, i, x, y, row, col, shift, color;
|
|
- ushort *pix;
|
|
-
|
|
- if (verbose) fprintf (stderr,_("Bilinear interpolation...\n"));
|
|
- if (filters == 9) size = 6;
|
|
- border_interpolate(1);
|
|
- for (row=0; row < size; row++)
|
|
- for (col=0; col < size; col++) {
|
|
- ip = code[row][col]+1;
|
|
- f = fcol(row,col);
|
|
- memset (sum, 0, sizeof sum);
|
|
- for (y=-1; y <= 1; y++)
|
|
- for (x=-1; x <= 1; x++) {
|
|
- shift = (y==0) + (x==0);
|
|
- color = fcol(row+y,col+x);
|
|
- if (color == f) continue;
|
|
- *ip++ = (width*y + x)*4 + color;
|
|
- *ip++ = shift;
|
|
- *ip++ = color;
|
|
- sum[color] += 1 << shift;
|
|
- }
|
|
- code[row][col][0] = (ip - code[row][col]) / 3;
|
|
- FORCC
|
|
- if (c != f) {
|
|
- *ip++ = c;
|
|
- *ip++ = 256 / sum[c];
|
|
- }
|
|
- }
|
|
- for (row=1; row < height-1; row++)
|
|
- for (col=1; col < width-1; col++) {
|
|
- pix = image[row*width+col];
|
|
- ip = code[row % size][col % size];
|
|
- memset (sum, 0, sizeof sum);
|
|
- for (i=*ip++; i--; ip+=3)
|
|
- sum[ip[2]] += pix[ip[0]] << ip[1];
|
|
- for (i=colors; --i; ip+=2)
|
|
- pix[ip[0]] = sum[ip[0]] * ip[1] >> 8;
|
|
- }
|
|
-}
|
|
-
|
|
-/*
|
|
- This algorithm is officially called:
|
|
-
|
|
- "Interpolation using a Threshold-based variable number of gradients"
|
|
-
|
|
- described in http://scien.stanford.edu/pages/labsite/1999/psych221/projects/99/tingchen/algodep/vargra.html
|
|
-
|
|
- I've extended the basic idea to work with non-Bayer filter arrays.
|
|
- Gradients are numbered clockwise from NW=0 to W=7.
|
|
- */
|
|
-void CLASS vng_interpolate()
|
|
-{
|
|
- static const signed char *cp, terms[] = {
|
|
- -2,-2,+0,-1,0,0x01, -2,-2,+0,+0,1,0x01, -2,-1,-1,+0,0,0x01,
|
|
- -2,-1,+0,-1,0,0x02, -2,-1,+0,+0,0,0x03, -2,-1,+0,+1,1,0x01,
|
|
- -2,+0,+0,-1,0,0x06, -2,+0,+0,+0,1,0x02, -2,+0,+0,+1,0,0x03,
|
|
- -2,+1,-1,+0,0,0x04, -2,+1,+0,-1,1,0x04, -2,+1,+0,+0,0,0x06,
|
|
- -2,+1,+0,+1,0,0x02, -2,+2,+0,+0,1,0x04, -2,+2,+0,+1,0,0x04,
|
|
- -1,-2,-1,+0,0,0x80, -1,-2,+0,-1,0,0x01, -1,-2,+1,-1,0,0x01,
|
|
- -1,-2,+1,+0,1,0x01, -1,-1,-1,+1,0,0x88, -1,-1,+1,-2,0,0x40,
|
|
- -1,-1,+1,-1,0,0x22, -1,-1,+1,+0,0,0x33, -1,-1,+1,+1,1,0x11,
|
|
- -1,+0,-1,+2,0,0x08, -1,+0,+0,-1,0,0x44, -1,+0,+0,+1,0,0x11,
|
|
- -1,+0,+1,-2,1,0x40, -1,+0,+1,-1,0,0x66, -1,+0,+1,+0,1,0x22,
|
|
- -1,+0,+1,+1,0,0x33, -1,+0,+1,+2,1,0x10, -1,+1,+1,-1,1,0x44,
|
|
- -1,+1,+1,+0,0,0x66, -1,+1,+1,+1,0,0x22, -1,+1,+1,+2,0,0x10,
|
|
- -1,+2,+0,+1,0,0x04, -1,+2,+1,+0,1,0x04, -1,+2,+1,+1,0,0x04,
|
|
- +0,-2,+0,+0,1,0x80, +0,-1,+0,+1,1,0x88, +0,-1,+1,-2,0,0x40,
|
|
- +0,-1,+1,+0,0,0x11, +0,-1,+2,-2,0,0x40, +0,-1,+2,-1,0,0x20,
|
|
- +0,-1,+2,+0,0,0x30, +0,-1,+2,+1,1,0x10, +0,+0,+0,+2,1,0x08,
|
|
- +0,+0,+2,-2,1,0x40, +0,+0,+2,-1,0,0x60, +0,+0,+2,+0,1,0x20,
|
|
- +0,+0,+2,+1,0,0x30, +0,+0,+2,+2,1,0x10, +0,+1,+1,+0,0,0x44,
|
|
- +0,+1,+1,+2,0,0x10, +0,+1,+2,-1,1,0x40, +0,+1,+2,+0,0,0x60,
|
|
- +0,+1,+2,+1,0,0x20, +0,+1,+2,+2,0,0x10, +1,-2,+1,+0,0,0x80,
|
|
- +1,-1,+1,+1,0,0x88, +1,+0,+1,+2,0,0x08, +1,+0,+2,-1,0,0x40,
|
|
- +1,+0,+2,+1,0,0x10
|
|
- }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
|
|
- ushort (*brow[5])[4], *pix;
|
|
- int prow=8, pcol=2, *ip, *code[16][16], gval[8], gmin, gmax, sum[4];
|
|
- int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
|
|
- int g, diff, thold, num, c;
|
|
-
|
|
- lin_interpolate();
|
|
- if (verbose) fprintf (stderr,_("VNG interpolation...\n"));
|
|
-
|
|
- if (filters == 1) prow = pcol = 16;
|
|
- if (filters == 9) prow = pcol = 6;
|
|
- ip = (int *) calloc (prow*pcol, 1280);
|
|
- merror (ip, "vng_interpolate()");
|
|
- for (row=0; row < prow; row++) /* Precalculate for VNG */
|
|
- for (col=0; col < pcol; col++) {
|
|
- code[row][col] = ip;
|
|
- for (cp=terms, t=0; t < 64; t++) {
|
|
- y1 = *cp++; x1 = *cp++;
|
|
- y2 = *cp++; x2 = *cp++;
|
|
- weight = *cp++;
|
|
- grads = *cp++;
|
|
- color = fcol(row+y1,col+x1);
|
|
- if (fcol(row+y2,col+x2) != color) continue;
|
|
- diag = (fcol(row,col+1) == color && fcol(row+1,col) == color) ? 2:1;
|
|
- if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue;
|
|
- *ip++ = (y1*width + x1)*4 + color;
|
|
- *ip++ = (y2*width + x2)*4 + color;
|
|
- *ip++ = weight;
|
|
- for (g=0; g < 8; g++)
|
|
- if (grads & 1<<g) *ip++ = g;
|
|
- *ip++ = -1;
|
|
- }
|
|
- *ip++ = INT_MAX;
|
|
- for (cp=chood, g=0; g < 8; g++) {
|
|
- y = *cp++; x = *cp++;
|
|
- *ip++ = (y*width + x) * 4;
|
|
- color = fcol(row,col);
|
|
- if (fcol(row+y,col+x) != color && fcol(row+y*2,col+x*2) == color)
|
|
- *ip++ = (y*width + x) * 8 + color;
|
|
- else
|
|
- *ip++ = 0;
|
|
- }
|
|
- }
|
|
- brow[4] = (ushort (*)[4]) calloc (width*3, sizeof **brow);
|
|
- merror (brow[4], "vng_interpolate()");
|
|
- for (row=0; row < 3; row++)
|
|
- brow[row] = brow[4] + row*width;
|
|
- for (row=2; row < height-2; row++) { /* Do VNG interpolation */
|
|
- for (col=2; col < width-2; col++) {
|
|
- pix = image[row*width+col];
|
|
- ip = code[row % prow][col % pcol];
|
|
- memset (gval, 0, sizeof gval);
|
|
- while ((g = ip[0]) != INT_MAX) { /* Calculate gradients */
|
|
- diff = ABS(pix[g] - pix[ip[1]]) << ip[2];
|
|
- gval[ip[3]] += diff;
|
|
- ip += 5;
|
|
- if ((g = ip[-1]) == -1) continue;
|
|
- gval[g] += diff;
|
|
- while ((g = *ip++) != -1)
|
|
- gval[g] += diff;
|
|
- }
|
|
- ip++;
|
|
- gmin = gmax = gval[0]; /* Choose a threshold */
|
|
- for (g=1; g < 8; g++) {
|
|
- if (gmin > gval[g]) gmin = gval[g];
|
|
- if (gmax < gval[g]) gmax = gval[g];
|
|
- }
|
|
- if (gmax == 0) {
|
|
- memcpy (brow[2][col], pix, sizeof *image);
|
|
- continue;
|
|
- }
|
|
- thold = gmin + (gmax >> 1);
|
|
- memset (sum, 0, sizeof sum);
|
|
- color = fcol(row,col);
|
|
- for (num=g=0; g < 8; g++,ip+=2) { /* Average the neighbors */
|
|
- if (gval[g] <= thold) {
|
|
- FORCC
|
|
- if (c == color && ip[1])
|
|
- sum[c] += (pix[c] + pix[ip[1]]) >> 1;
|
|
- else
|
|
- sum[c] += pix[ip[0] + c];
|
|
- num++;
|
|
- }
|
|
- }
|
|
- FORCC { /* Save to buffer */
|
|
- t = pix[color];
|
|
- if (c != color)
|
|
- t += (sum[c] - sum[color]) / num;
|
|
- brow[2][col][c] = CLIP(t);
|
|
- }
|
|
- }
|
|
- if (row > 3) /* Write buffer to image */
|
|
- memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
|
|
- for (g=0; g < 4; g++)
|
|
- brow[(g-1) & 3] = brow[g];
|
|
- }
|
|
- memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
|
|
- memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image);
|
|
- free (brow[4]);
|
|
- free (code[0][0]);
|
|
-}
|
|
-
|
|
-/*
|
|
- Patterned Pixel Grouping Interpolation by Alain Desbiolles
|
|
-*/
|
|
-void CLASS ppg_interpolate()
|
|
-{
|
|
- int dir[5] = { 1, width, -1, -width, 1 };
|
|
- int row, col, diff[2], guess[2], c, d, i;
|
|
- ushort (*pix)[4];
|
|
-
|
|
- border_interpolate(3);
|
|
- if (verbose) fprintf (stderr,_("PPG interpolation...\n"));
|
|
-
|
|
-/* Fill in the green layer with gradients and pattern recognition: */
|
|
- for (row=3; row < height-3; row++)
|
|
- for (col=3+(FC(row,3) & 1), c=FC(row,col); col < width-3; col+=2) {
|
|
- pix = image + row*width+col;
|
|
- for (i=0; (d=dir[i]) > 0; i++) {
|
|
- guess[i] = (pix[-d][1] + pix[0][c] + pix[d][1]) * 2
|
|
- - pix[-2*d][c] - pix[2*d][c];
|
|
- diff[i] = ( ABS(pix[-2*d][c] - pix[ 0][c]) +
|
|
- ABS(pix[ 2*d][c] - pix[ 0][c]) +
|
|
- ABS(pix[ -d][1] - pix[ d][1]) ) * 3 +
|
|
- ( ABS(pix[ 3*d][1] - pix[ d][1]) +
|
|
- ABS(pix[-3*d][1] - pix[-d][1]) ) * 2;
|
|
- }
|
|
- d = dir[i = diff[0] > diff[1]];
|
|
- pix[0][1] = ULIM(guess[i] >> 2, pix[d][1], pix[-d][1]);
|
|
- }
|
|
-/* Calculate red and blue for each green pixel: */
|
|
- for (row=1; row < height-1; row++)
|
|
- for (col=1+(FC(row,2) & 1), c=FC(row,col+1); col < width-1; col+=2) {
|
|
- pix = image + row*width+col;
|
|
- for (i=0; (d=dir[i]) > 0; c=2-c, i++)
|
|
- pix[0][c] = CLIP((pix[-d][c] + pix[d][c] + 2*pix[0][1]
|
|
- - pix[-d][1] - pix[d][1]) >> 1);
|
|
- }
|
|
-/* Calculate blue for red pixels and vice versa: */
|
|
- for (row=1; row < height-1; row++)
|
|
- for (col=1+(FC(row,1) & 1), c=2-FC(row,col); col < width-1; col+=2) {
|
|
- pix = image + row*width+col;
|
|
- for (i=0; (d=dir[i]+dir[i+1]) > 0; i++) {
|
|
- diff[i] = ABS(pix[-d][c] - pix[d][c]) +
|
|
- ABS(pix[-d][1] - pix[0][1]) +
|
|
- ABS(pix[ d][1] - pix[0][1]);
|
|
- guess[i] = pix[-d][c] + pix[d][c] + 2*pix[0][1]
|
|
- - pix[-d][1] - pix[d][1];
|
|
- }
|
|
- if (diff[0] != diff[1])
|
|
- pix[0][c] = CLIP(guess[diff[0] > diff[1]] >> 1);
|
|
- else
|
|
- pix[0][c] = CLIP((guess[0]+guess[1]) >> 2);
|
|
- }
|
|
-}
|
|
-
|
|
-void CLASS cielab (ushort rgb[3], short lab[3])
|
|
-{
|
|
- int c, i, j, k;
|
|
- float r, xyz[3];
|
|
- static float cbrt[0x10000], xyz_cam[3][4];
|
|
-
|
|
- if (!rgb) {
|
|
- for (i=0; i < 0x10000; i++) {
|
|
- r = i / 65535.0;
|
|
- cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0;
|
|
- }
|
|
- for (i=0; i < 3; i++)
|
|
- for (j=0; j < colors; j++)
|
|
- for (xyz_cam[i][j] = k=0; k < 3; k++)
|
|
- xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i];
|
|
- return;
|
|
- }
|
|
- xyz[0] = xyz[1] = xyz[2] = 0.5;
|
|
- FORCC {
|
|
- xyz[0] += xyz_cam[0][c] * rgb[c];
|
|
- xyz[1] += xyz_cam[1][c] * rgb[c];
|
|
- xyz[2] += xyz_cam[2][c] * rgb[c];
|
|
- }
|
|
- xyz[0] = cbrt[CLIP((int) xyz[0])];
|
|
- xyz[1] = cbrt[CLIP((int) xyz[1])];
|
|
- xyz[2] = cbrt[CLIP((int) xyz[2])];
|
|
- lab[0] = 64 * (116 * xyz[1] - 16);
|
|
- lab[1] = 64 * 500 * (xyz[0] - xyz[1]);
|
|
- lab[2] = 64 * 200 * (xyz[1] - xyz[2]);
|
|
-}
|
|
-
|
|
-#define TS 512 /* Tile Size */
|
|
-#define fcol(row,col) xtrans[(row+6) % 6][(col+6) % 6]
|
|
-
|
|
-/*
|
|
- Frank Markesteijn's algorithm for Fuji X-Trans sensors
|
|
- */
|
|
-void CLASS xtrans_interpolate (int passes)
|
|
-{
|
|
- int c, d, f, g, h, i, v, ng, row, col, top, left, mrow, mcol;
|
|
- int val, ndir, pass, hm[8], avg[4], color[3][8];
|
|
- static const short orth[12] = { 1,0,0,1,-1,0,0,-1,1,0,0,1 },
|
|
- patt[2][16] = { { 0,1,0,-1,2,0,-1,0,1,1,1,-1,0,0,0,0 },
|
|
- { 0,1,0,-2,1,0,-2,0,1,1,-2,-2,1,-1,-1,1 } },
|
|
- dir[4] = { 1,TS,TS+1,TS-1 };
|
|
- short allhex[3][3][2][8], *hex;
|
|
- ushort min, max, sgrow, sgcol;
|
|
- ushort (*rgb)[TS][TS][3], (*rix)[3], (*pix)[4];
|
|
- short (*lab) [TS][3], (*lix)[3];
|
|
- float (*drv)[TS][TS], diff[6], tr;
|
|
- char (*homo)[TS][TS], *buffer;
|
|
-
|
|
- if (verbose)
|
|
- fprintf (stderr,_("%d-pass X-Trans interpolation...\n"), passes);
|
|
-
|
|
- cielab (0,0);
|
|
- ndir = 4 << (passes > 1);
|
|
- buffer = (char *) malloc (TS*TS*(ndir*11+6));
|
|
- merror (buffer, "xtrans_interpolate()");
|
|
- rgb = (ushort(*)[TS][TS][3]) buffer;
|
|
- lab = (short (*) [TS][3])(buffer + TS*TS*(ndir*6));
|
|
- drv = (float (*)[TS][TS]) (buffer + TS*TS*(ndir*6+6));
|
|
- homo = (char (*)[TS][TS]) (buffer + TS*TS*(ndir*10+6));
|
|
-
|
|
-/* Map a green hexagon around each non-green pixel and vice versa: */
|
|
- for (row=0; row < 3; row++)
|
|
- for (col=0; col < 3; col++)
|
|
- for (ng=d=0; d < 10; d+=2) {
|
|
- g = fcol(row,col) == 1;
|
|
- if (fcol(row+orth[d],col+orth[d+2]) == 1) ng=0; else ng++;
|
|
- if (ng == 4) { sgrow = row; sgcol = col; }
|
|
- if (ng == g+1) FORC(8) {
|
|
- v = orth[d ]*patt[g][c*2] + orth[d+1]*patt[g][c*2+1];
|
|
- h = orth[d+2]*patt[g][c*2] + orth[d+3]*patt[g][c*2+1];
|
|
- allhex[row][col][0][c^(g*2 & d)] = h + v*width;
|
|
- allhex[row][col][1][c^(g*2 & d)] = h + v*TS;
|
|
- }
|
|
- }
|
|
-
|
|
-/* Set green1 and green3 to the minimum and maximum allowed values: */
|
|
- for (row=2; row < height-2; row++)
|
|
- for (min=~(max=0), col=2; col < width-2; col++) {
|
|
- if (fcol(row,col) == 1 && (min=~(max=0))) continue;
|
|
- pix = image + row*width + col;
|
|
- hex = allhex[row % 3][col % 3][0];
|
|
- if (!max) FORC(6) {
|
|
- val = pix[hex[c]][1];
|
|
- if (min > val) min = val;
|
|
- if (max < val) max = val;
|
|
- }
|
|
- pix[0][1] = min;
|
|
- pix[0][3] = max;
|
|
- switch ((row-sgrow) % 3) {
|
|
- case 1: if (row < height-3) { row++; col--; } break;
|
|
- case 2: if ((min=~(max=0)) && (col+=2) < width-3 && row > 2) row--;
|
|
- }
|
|
- }
|
|
-
|
|
- for (top=3; top < height-19; top += TS-16)
|
|
- for (left=3; left < width-19; left += TS-16) {
|
|
- mrow = MIN (top+TS, height-3);
|
|
- mcol = MIN (left+TS, width-3);
|
|
- for (row=top; row < mrow; row++)
|
|
- for (col=left; col < mcol; col++)
|
|
- memcpy (rgb[0][row-top][col-left], image[row*width+col], 6);
|
|
- FORC3 memcpy (rgb[c+1], rgb[0], sizeof *rgb);
|
|
-
|
|
-/* Interpolate green horizontally, vertically, and along both diagonals: */
|
|
- for (row=top; row < mrow; row++)
|
|
- for (col=left; col < mcol; col++) {
|
|
- if ((f = fcol(row,col)) == 1) continue;
|
|
- pix = image + row*width + col;
|
|
- hex = allhex[row % 3][col % 3][0];
|
|
- color[1][0] = 174 * (pix[ hex[1]][1] + pix[ hex[0]][1]) -
|
|
- 46 * (pix[2*hex[1]][1] + pix[2*hex[0]][1]);
|
|
- color[1][1] = 223 * pix[ hex[3]][1] + pix[ hex[2]][1] * 33 +
|
|
- 92 * (pix[ 0 ][f] - pix[ -hex[2]][f]);
|
|
- FORC(2) color[1][2+c] =
|
|
- 164 * pix[hex[4+c]][1] + 92 * pix[-2*hex[4+c]][1] + 33 *
|
|
- (2*pix[0][f] - pix[3*hex[4+c]][f] - pix[-3*hex[4+c]][f]);
|
|
- FORC4 rgb[c^!((row-sgrow) % 3)][row-top][col-left][1] =
|
|
- LIM(color[1][c] >> 8,pix[0][1],pix[0][3]);
|
|
- }
|
|
-
|
|
- for (pass=0; pass < passes; pass++) {
|
|
- if (pass == 1)
|
|
- memcpy (rgb+=4, buffer, 4*sizeof *rgb);
|
|
-
|
|
-/* Recalculate green from interpolated values of closer pixels: */
|
|
- if (pass) {
|
|
- for (row=top+2; row < mrow-2; row++)
|
|
- for (col=left+2; col < mcol-2; col++) {
|
|
- if ((f = fcol(row,col)) == 1) continue;
|
|
- pix = image + row*width + col;
|
|
- hex = allhex[row % 3][col % 3][1];
|
|
- for (d=3; d < 6; d++) {
|
|
- rix = &rgb[(d-2)^!((row-sgrow) % 3)][row-top][col-left];
|
|
- val = rix[-2*hex[d]][1] + 2*rix[hex[d]][1]
|
|
- - rix[-2*hex[d]][f] - 2*rix[hex[d]][f] + 3*rix[0][f];
|
|
- rix[0][1] = LIM(val/3,pix[0][1],pix[0][3]);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
-/* Interpolate red and blue values for solitary green pixels: */
|
|
- for (row=(top-sgrow+4)/3*3+sgrow; row < mrow-2; row+=3)
|
|
- for (col=(left-sgcol+4)/3*3+sgcol; col < mcol-2; col+=3) {
|
|
- rix = &rgb[0][row-top][col-left];
|
|
- h = fcol(row,col+1);
|
|
- memset (diff, 0, sizeof diff);
|
|
- for (i=1, d=0; d < 6; d++, i^=TS^1, h^=2) {
|
|
- for (c=0; c < 2; c++, h^=2) {
|
|
- g = 2*rix[0][1] - rix[i<<c][1] - rix[-i<<c][1];
|
|
- color[h][d] = g + rix[i<<c][h] + rix[-i<<c][h];
|
|
- if (d > 1)
|
|
- diff[d] += SQR (rix[i<<c][1] - rix[-i<<c][1]
|
|
- - rix[i<<c][h] + rix[-i<<c][h]) + SQR(g);
|
|
- }
|
|
- if (d > 1 && (d & 1))
|
|
- if (diff[d-1] < diff[d])
|
|
- FORC(2) color[c*2][d] = color[c*2][d-1];
|
|
- if (d < 2 || (d & 1)) {
|
|
- FORC(2) rix[0][c*2] = CLIP(color[c*2][d]/2);
|
|
- rix += TS*TS;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
-/* Interpolate red for blue pixels and vice versa: */
|
|
- for (row=top+3; row < mrow-3; row++)
|
|
- for (col=left+3; col < mcol-3; col++) {
|
|
- if ((f = 2-fcol(row,col)) == 1) continue;
|
|
- rix = &rgb[0][row-top][col-left];
|
|
- c = (row-sgrow) % 3 ? TS:1;
|
|
- h = 3 * (c ^ TS ^ 1);
|
|
- for (d=0; d < 4; d++, rix += TS*TS) {
|
|
- i = d > 1 || ((d ^ c) & 1) ||
|
|
- ((ABS(rix[0][1]-rix[c][1])+ABS(rix[0][1]-rix[-c][1])) <
|
|
- 2*(ABS(rix[0][1]-rix[h][1])+ABS(rix[0][1]-rix[-h][1]))) ? c:h;
|
|
- rix[0][f] = CLIP((rix[i][f] + rix[-i][f] +
|
|
- 2*rix[0][1] - rix[i][1] - rix[-i][1])/2);
|
|
- }
|
|
- }
|
|
-
|
|
-/* Fill in red and blue for 2x2 blocks of green: */
|
|
- for (row=top+2; row < mrow-2; row++) if ((row-sgrow) % 3)
|
|
- for (col=left+2; col < mcol-2; col++) if ((col-sgcol) % 3) {
|
|
- rix = &rgb[0][row-top][col-left];
|
|
- hex = allhex[row % 3][col % 3][1];
|
|
- for (d=0; d < ndir; d+=2, rix += TS*TS)
|
|
- if (hex[d] + hex[d+1]) {
|
|
- g = 3*rix[0][1] - 2*rix[hex[d]][1] - rix[hex[d+1]][1];
|
|
- for (c=0; c < 4; c+=2) rix[0][c] =
|
|
- CLIP((g + 2*rix[hex[d]][c] + rix[hex[d+1]][c])/3);
|
|
- } else {
|
|
- g = 2*rix[0][1] - rix[hex[d]][1] - rix[hex[d+1]][1];
|
|
- for (c=0; c < 4; c+=2) rix[0][c] =
|
|
- CLIP((g + rix[hex[d]][c] + rix[hex[d+1]][c])/2);
|
|
- }
|
|
- }
|
|
- }
|
|
- rgb = (ushort(*)[TS][TS][3]) buffer;
|
|
- mrow -= top;
|
|
- mcol -= left;
|
|
-
|
|
-/* Convert to CIELab and differentiate in all directions: */
|
|
- for (d=0; d < ndir; d++) {
|
|
- for (row=2; row < mrow-2; row++)
|
|
- for (col=2; col < mcol-2; col++)
|
|
- cielab (rgb[d][row][col], lab[row][col]);
|
|
- for (f=dir[d & 3],row=3; row < mrow-3; row++)
|
|
- for (col=3; col < mcol-3; col++) {
|
|
- lix = &lab[row][col];
|
|
- g = 2*lix[0][0] - lix[f][0] - lix[-f][0];
|
|
- drv[d][row][col] = SQR(g)
|
|
- + SQR((2*lix[0][1] - lix[f][1] - lix[-f][1] + g*500/232))
|
|
- + SQR((2*lix[0][2] - lix[f][2] - lix[-f][2] - g*500/580));
|
|
- }
|
|
- }
|
|
-
|
|
-/* Build homogeneity maps from the derivatives: */
|
|
- memset(homo, 0, ndir*TS*TS);
|
|
- for (row=4; row < mrow-4; row++)
|
|
- for (col=4; col < mcol-4; col++) {
|
|
- for (tr=FLT_MAX, d=0; d < ndir; d++)
|
|
- if (tr > drv[d][row][col])
|
|
- tr = drv[d][row][col];
|
|
- tr *= 8;
|
|
- for (d=0; d < ndir; d++)
|
|
- for (v=-1; v <= 1; v++)
|
|
- for (h=-1; h <= 1; h++)
|
|
- if (drv[d][row+v][col+h] <= tr)
|
|
- homo[d][row][col]++;
|
|
- }
|
|
-
|
|
-/* Average the most homogenous pixels for the final result: */
|
|
- if (height-top < TS+4) mrow = height-top+2;
|
|
- if (width-left < TS+4) mcol = width-left+2;
|
|
- for (row = MIN(top,8); row < mrow-8; row++)
|
|
- for (col = MIN(left,8); col < mcol-8; col++) {
|
|
- for (d=0; d < ndir; d++)
|
|
- for (hm[d]=0, v=-2; v <= 2; v++)
|
|
- for (h=-2; h <= 2; h++)
|
|
- hm[d] += homo[d][row+v][col+h];
|
|
- for (d=0; d < ndir-4; d++)
|
|
- if (hm[d] < hm[d+4]) hm[d ] = 0; else
|
|
- if (hm[d] > hm[d+4]) hm[d+4] = 0;
|
|
- for (max=hm[0],d=1; d < ndir; d++)
|
|
- if (max < hm[d]) max = hm[d];
|
|
- max -= max >> 3;
|
|
- memset (avg, 0, sizeof avg);
|
|
- for (d=0; d < ndir; d++)
|
|
- if (hm[d] >= max) {
|
|
- FORC3 avg[c] += rgb[d][row][col][c];
|
|
- avg[3]++;
|
|
- }
|
|
- FORC3 image[(row+top)*width+col+left][c] = avg[c]/avg[3];
|
|
- }
|
|
- }
|
|
- free(buffer);
|
|
- border_interpolate(8);
|
|
-}
|
|
-#undef fcol
|
|
-
|
|
-/*
|
|
- Adaptive Homogeneity-Directed interpolation is based on
|
|
- the work of Keigo Hirakawa, Thomas Parks, and Paul Lee.
|
|
- */
|
|
-void CLASS ahd_interpolate()
|
|
-{
|
|
- int i, j, top, left, row, col, tr, tc, c, d, val, hm[2];
|
|
- static const int dir[4] = { -1, 1, -TS, TS };
|
|
- unsigned ldiff[2][4], abdiff[2][4], leps, abeps;
|
|
- ushort (*rgb)[TS][TS][3], (*rix)[3], (*pix)[4];
|
|
- short (*lab)[TS][TS][3], (*lix)[3];
|
|
- char (*homo)[TS][TS], *buffer;
|
|
-
|
|
- if (verbose) fprintf (stderr,_("AHD interpolation...\n"));
|
|
-
|
|
- cielab (0,0);
|
|
- border_interpolate(5);
|
|
- buffer = (char *) malloc (26*TS*TS);
|
|
- merror (buffer, "ahd_interpolate()");
|
|
- rgb = (ushort(*)[TS][TS][3]) buffer;
|
|
- lab = (short (*)[TS][TS][3])(buffer + 12*TS*TS);
|
|
- homo = (char (*)[TS][TS]) (buffer + 24*TS*TS);
|
|
-
|
|
- for (top=2; top < height-5; top += TS-6)
|
|
- for (left=2; left < width-5; left += TS-6) {
|
|
-
|
|
-/* Interpolate green horizontally and vertically: */
|
|
- for (row=top; row < top+TS && row < height-2; row++) {
|
|
- col = left + (FC(row,left) & 1);
|
|
- for (c = FC(row,col); col < left+TS && col < width-2; col+=2) {
|
|
- pix = image + row*width+col;
|
|
- val = ((pix[-1][1] + pix[0][c] + pix[1][1]) * 2
|
|
- - pix[-2][c] - pix[2][c]) >> 2;
|
|
- rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]);
|
|
- val = ((pix[-width][1] + pix[0][c] + pix[width][1]) * 2
|
|
- - pix[-2*width][c] - pix[2*width][c]) >> 2;
|
|
- rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]);
|
|
- }
|
|
- }
|
|
-/* Interpolate red and blue, and convert to CIELab: */
|
|
- for (d=0; d < 2; d++)
|
|
- for (row=top+1; row < top+TS-1 && row < height-3; row++)
|
|
- for (col=left+1; col < left+TS-1 && col < width-3; col++) {
|
|
- pix = image + row*width+col;
|
|
- rix = &rgb[d][row-top][col-left];
|
|
- lix = &lab[d][row-top][col-left];
|
|
- if ((c = 2 - FC(row,col)) == 1) {
|
|
- c = FC(row+1,col);
|
|
- val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c]
|
|
- - rix[-1][1] - rix[1][1] ) >> 1);
|
|
- rix[0][2-c] = CLIP(val);
|
|
- val = pix[0][1] + (( pix[-width][c] + pix[width][c]
|
|
- - rix[-TS][1] - rix[TS][1] ) >> 1);
|
|
- } else
|
|
- val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c]
|
|
- + pix[+width-1][c] + pix[+width+1][c]
|
|
- - rix[-TS-1][1] - rix[-TS+1][1]
|
|
- - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2);
|
|
- rix[0][c] = CLIP(val);
|
|
- c = FC(row,col);
|
|
- rix[0][c] = pix[0][c];
|
|
- cielab (rix[0],lix[0]);
|
|
- }
|
|
-/* Build homogeneity maps from the CIELab images: */
|
|
- memset (homo, 0, 2*TS*TS);
|
|
- for (row=top+2; row < top+TS-2 && row < height-4; row++) {
|
|
- tr = row-top;
|
|
- for (col=left+2; col < left+TS-2 && col < width-4; col++) {
|
|
- tc = col-left;
|
|
- for (d=0; d < 2; d++) {
|
|
- lix = &lab[d][tr][tc];
|
|
- for (i=0; i < 4; i++) {
|
|
- ldiff[d][i] = ABS(lix[0][0]-lix[dir[i]][0]);
|
|
- abdiff[d][i] = SQR(lix[0][1]-lix[dir[i]][1])
|
|
- + SQR(lix[0][2]-lix[dir[i]][2]);
|
|
- }
|
|
- }
|
|
- leps = MIN(MAX(ldiff[0][0],ldiff[0][1]),
|
|
- MAX(ldiff[1][2],ldiff[1][3]));
|
|
- abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]),
|
|
- MAX(abdiff[1][2],abdiff[1][3]));
|
|
- for (d=0; d < 2; d++)
|
|
- for (i=0; i < 4; i++)
|
|
- if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps)
|
|
- homo[d][tr][tc]++;
|
|
- }
|
|
- }
|
|
-/* Combine the most homogenous pixels for the final result: */
|
|
- for (row=top+3; row < top+TS-3 && row < height-5; row++) {
|
|
- tr = row-top;
|
|
- for (col=left+3; col < left+TS-3 && col < width-5; col++) {
|
|
- tc = col-left;
|
|
- for (d=0; d < 2; d++)
|
|
- for (hm[d]=0, i=tr-1; i <= tr+1; i++)
|
|
- for (j=tc-1; j <= tc+1; j++)
|
|
- hm[d] += homo[d][i][j];
|
|
- if (hm[0] != hm[1])
|
|
- FORC3 image[row*width+col][c] = rgb[hm[1] > hm[0]][tr][tc][c];
|
|
- else
|
|
- FORC3 image[row*width+col][c] =
|
|
- (rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1;
|
|
- }
|
|
- }
|
|
- }
|
|
- free (buffer);
|
|
-}
|
|
-#undef TS
|
|
-
|
|
-void CLASS median_filter()
|
|
-{
|
|
- ushort (*pix)[4];
|
|
- int pass, c, i, j, k, med[9];
|
|
- static const uchar opt[] = /* Optimal 9-element median search */
|
|
- { 1,2, 4,5, 7,8, 0,1, 3,4, 6,7, 1,2, 4,5, 7,8,
|
|
- 0,3, 5,8, 4,7, 3,6, 1,4, 2,5, 4,7, 4,2, 6,4, 4,2 };
|
|
-
|
|
- for (pass=1; pass <= med_passes; pass++) {
|
|
- if (verbose)
|
|
- fprintf (stderr,_("Median filter pass %d...\n"), pass);
|
|
- for (c=0; c < 3; c+=2) {
|
|
- for (pix = image; pix < image+width*height; pix++)
|
|
- pix[0][3] = pix[0][c];
|
|
- for (pix = image+width; pix < image+width*(height-1); pix++) {
|
|
- if ((pix-image+1) % width < 2) continue;
|
|
- for (k=0, i = -width; i <= width; i += width)
|
|
- for (j = i-1; j <= i+1; j++)
|
|
- med[k++] = pix[j][3] - pix[j][1];
|
|
- for (i=0; i < sizeof opt; i+=2)
|
|
- if (med[opt[i]] > med[opt[i+1]])
|
|
- SWAP (med[opt[i]] , med[opt[i+1]]);
|
|
- pix[0][c] = CLIP(med[4] + pix[0][1]);
|
|
- }
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-void CLASS blend_highlights()
|
|
-{
|
|
- int clip=INT_MAX, row, col, c, i, j;
|
|
- static const float trans[2][4][4] =
|
|
- { { { 1,1,1 }, { 1.7320508,-1.7320508,0 }, { -1,-1,2 } },
|
|
- { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } };
|
|
- static const float itrans[2][4][4] =
|
|
- { { { 1,0.8660254,-0.5 }, { 1,-0.8660254,-0.5 }, { 1,0,1 } },
|
|
- { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } };
|
|
- float cam[2][4], lab[2][4], sum[2], chratio;
|
|
-
|
|
- if ((unsigned) (colors-3) > 1) return;
|
|
- if (verbose) fprintf (stderr,_("Blending highlights...\n"));
|
|
- FORCC if (clip > (i = 65535*pre_mul[c])) clip = i;
|
|
- for (row=0; row < height; row++)
|
|
- for (col=0; col < width; col++) {
|
|
- FORCC if (image[row*width+col][c] > clip) break;
|
|
- if (c == colors) continue;
|
|
- FORCC {
|
|
- cam[0][c] = image[row*width+col][c];
|
|
- cam[1][c] = MIN(cam[0][c],clip);
|
|
- }
|
|
- for (i=0; i < 2; i++) {
|
|
- FORCC for (lab[i][c]=j=0; j < colors; j++)
|
|
- lab[i][c] += trans[colors-3][c][j] * cam[i][j];
|
|
- for (sum[i]=0,c=1; c < colors; c++)
|
|
- sum[i] += SQR(lab[i][c]);
|
|
- }
|
|
- chratio = sqrt(sum[1]/sum[0]);
|
|
- for (c=1; c < colors; c++)
|
|
- lab[0][c] *= chratio;
|
|
- FORCC for (cam[0][c]=j=0; j < colors; j++)
|
|
- cam[0][c] += itrans[colors-3][c][j] * lab[0][j];
|
|
- FORCC image[row*width+col][c] = cam[0][c] / colors;
|
|
- }
|
|
-}
|
|
-
|
|
-#define SCALE (4 >> shrink)
|
|
-void CLASS recover_highlights()
|
|
-{
|
|
- float *map, sum, wgt, grow;
|
|
- int hsat[4], count, spread, change, val, i;
|
|
- unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x;
|
|
- ushort *pixel;
|
|
- static const signed char dir[8][2] =
|
|
- { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} };
|
|
-
|
|
- if (verbose) fprintf (stderr,_("Rebuilding highlights...\n"));
|
|
-
|
|
- grow = pow (2, 4-highlight);
|
|
- FORCC hsat[c] = 32000 * pre_mul[c];
|
|
- for (kc=0, c=1; c < colors; c++)
|
|
- if (pre_mul[kc] < pre_mul[c]) kc = c;
|
|
- high = height / SCALE;
|
|
- wide = width / SCALE;
|
|
- map = (float *) calloc (high, wide*sizeof *map);
|
|
- merror (map, "recover_highlights()");
|
|
- FORCC if (c != kc) {
|
|
- memset (map, 0, high*wide*sizeof *map);
|
|
- for (mrow=0; mrow < high; mrow++)
|
|
- for (mcol=0; mcol < wide; mcol++) {
|
|
- sum = wgt = count = 0;
|
|
- for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++)
|
|
- for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) {
|
|
- pixel = image[row*width+col];
|
|
- if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000) {
|
|
- sum += pixel[c];
|
|
- wgt += pixel[kc];
|
|
- count++;
|
|
- }
|
|
- }
|
|
- if (count == SCALE*SCALE)
|
|
- map[mrow*wide+mcol] = sum / wgt;
|
|
- }
|
|
- for (spread = 32/grow; spread--; ) {
|
|
- for (mrow=0; mrow < high; mrow++)
|
|
- for (mcol=0; mcol < wide; mcol++) {
|
|
- if (map[mrow*wide+mcol]) continue;
|
|
- sum = count = 0;
|
|
- for (d=0; d < 8; d++) {
|
|
- y = mrow + dir[d][0];
|
|
- x = mcol + dir[d][1];
|
|
- if (y < high && x < wide && map[y*wide+x] > 0) {
|
|
- sum += (1 + (d & 1)) * map[y*wide+x];
|
|
- count += 1 + (d & 1);
|
|
- }
|
|
- }
|
|
- if (count > 3)
|
|
- map[mrow*wide+mcol] = - (sum+grow) / (count+grow);
|
|
- }
|
|
- for (change=i=0; i < high*wide; i++)
|
|
- if (map[i] < 0) {
|
|
- map[i] = -map[i];
|
|
- change = 1;
|
|
- }
|
|
- if (!change) break;
|
|
- }
|
|
- for (i=0; i < high*wide; i++)
|
|
- if (map[i] == 0) map[i] = 1;
|
|
- for (mrow=0; mrow < high; mrow++)
|
|
- for (mcol=0; mcol < wide; mcol++) {
|
|
- for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++)
|
|
- for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) {
|
|
- pixel = image[row*width+col];
|
|
- if (pixel[c] / hsat[c] > 1) {
|
|
- val = pixel[kc] * map[mrow*wide+mcol];
|
|
- if (pixel[c] < val) pixel[c] = CLIP(val);
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
- free (map);
|
|
-}
|
|
-#undef SCALE
|
|
+//void CLASS border_interpolate (int border)
|
|
+//{
|
|
+// unsigned row, col, y, x, f, c, sum[8];
|
|
+//
|
|
+// for (row=0; row < height; row++)
|
|
+// for (col=0; col < width; col++) {
|
|
+// if (col==border && row >= border && row < height-border)
|
|
+// col = width-border;
|
|
+// memset (sum, 0, sizeof sum);
|
|
+// for (y=row-1; y != row+2; y++)
|
|
+// for (x=col-1; x != col+2; x++)
|
|
+// if (y < height && x < width) {
|
|
+// f = fcol(y,x);
|
|
+// sum[f] += image[y*width+x][f];
|
|
+// sum[f+4]++;
|
|
+// }
|
|
+// f = fcol(row,col);
|
|
+// FORCC if (c != f && sum[c+4])
|
|
+// image[row*width+col][c] = sum[c] / sum[c+4];
|
|
+// }
|
|
+//}
|
|
+
|
|
+/* RT: delete interpolation functions */
|
|
+
|
|
+
|
|
+//void CLASS cielab (ushort rgb[3], short lab[3])
|
|
+//{
|
|
+// int c, i, j, k;
|
|
+// float r, xyz[3];
|
|
+// static float cbrt[0x10000], xyz_cam[3][4];
|
|
+//
|
|
+// if (!rgb) {
|
|
+// for (i=0; i < 0x10000; i++) {
|
|
+// r = i / 65535.0;
|
|
+// cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0;
|
|
+// }
|
|
+// for (i=0; i < 3; i++)
|
|
+// for (j=0; j < colors; j++)
|
|
+// for (xyz_cam[i][j] = k=0; k < 3; k++)
|
|
+// xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i];
|
|
+// return;
|
|
+// }
|
|
+// xyz[0] = xyz[1] = xyz[2] = 0.5;
|
|
+// FORCC {
|
|
+// xyz[0] += xyz_cam[0][c] * rgb[c];
|
|
+// xyz[1] += xyz_cam[1][c] * rgb[c];
|
|
+// xyz[2] += xyz_cam[2][c] * rgb[c];
|
|
+// }
|
|
+// xyz[0] = cbrt[CLIP((int) xyz[0])];
|
|
+// xyz[1] = cbrt[CLIP((int) xyz[1])];
|
|
+// xyz[2] = cbrt[CLIP((int) xyz[2])];
|
|
+// lab[0] = 64 * (116 * xyz[1] - 16);
|
|
+// lab[1] = 64 * 500 * (xyz[0] - xyz[1]);
|
|
+// lab[2] = 64 * 200 * (xyz[1] - xyz[2]);
|
|
+//}
|
|
+//
|
|
+//#define TS 512 /* Tile Size */
|
|
+//#define fcol(row,col) xtrans[(row+6) % 6][(col+6) % 6]
|
|
+//
|
|
+///*
|
|
+// Frank Markesteijn's algorithm for Fuji X-Trans sensors
|
|
+// */
|
|
+//void CLASS xtrans_interpolate (int passes)
|
|
+//{
|
|
+// int c, d, f, g, h, i, v, ng, row, col, top, left, mrow, mcol;
|
|
+// int val, ndir, pass, hm[8], avg[4], color[3][8];
|
|
+// static const short orth[12] = { 1,0,0,1,-1,0,0,-1,1,0,0,1 },
|
|
+// patt[2][16] = { { 0,1,0,-1,2,0,-1,0,1,1,1,-1,0,0,0,0 },
|
|
+// { 0,1,0,-2,1,0,-2,0,1,1,-2,-2,1,-1,-1,1 } },
|
|
+// dir[4] = { 1,TS,TS+1,TS-1 };
|
|
+// short allhex[3][3][2][8], *hex;
|
|
+// ushort min, max, sgrow, sgcol;
|
|
+// ushort (*rgb)[TS][TS][3], (*rix)[3], (*pix)[4];
|
|
+// short (*lab) [TS][3], (*lix)[3];
|
|
+// float (*drv)[TS][TS], diff[6], tr;
|
|
+// char (*homo)[TS][TS], *buffer;
|
|
+//
|
|
+// if (verbose)
|
|
+// fprintf (stderr,_("%d-pass X-Trans interpolation...\n"), passes);
|
|
+//
|
|
+// cielab (0,0);
|
|
+// ndir = 4 << (passes > 1);
|
|
+// buffer = (char *) malloc (TS*TS*(ndir*11+6));
|
|
+// merror (buffer, "xtrans_interpolate()");
|
|
+// rgb = (ushort(*)[TS][TS][3]) buffer;
|
|
+// lab = (short (*) [TS][3])(buffer + TS*TS*(ndir*6));
|
|
+// drv = (float (*)[TS][TS]) (buffer + TS*TS*(ndir*6+6));
|
|
+// homo = (char (*)[TS][TS]) (buffer + TS*TS*(ndir*10+6));
|
|
+//
|
|
+///* Map a green hexagon around each non-green pixel and vice versa: */
|
|
+// for (row=0; row < 3; row++)
|
|
+// for (col=0; col < 3; col++)
|
|
+// for (ng=d=0; d < 10; d+=2) {
|
|
+// g = fcol(row,col) == 1;
|
|
+// if (fcol(row+orth[d],col+orth[d+2]) == 1) ng=0; else ng++;
|
|
+// if (ng == 4) { sgrow = row; sgcol = col; }
|
|
+// if (ng == g+1) FORC(8) {
|
|
+// v = orth[d ]*patt[g][c*2] + orth[d+1]*patt[g][c*2+1];
|
|
+// h = orth[d+2]*patt[g][c*2] + orth[d+3]*patt[g][c*2+1];
|
|
+// allhex[row][col][0][c^(g*2 & d)] = h + v*width;
|
|
+// allhex[row][col][1][c^(g*2 & d)] = h + v*TS;
|
|
+// }
|
|
+// }
|
|
+//
|
|
+///* Set green1 and green3 to the minimum and maximum allowed values: */
|
|
+// for (row=2; row < height-2; row++)
|
|
+// for (min=~(max=0), col=2; col < width-2; col++) {
|
|
+// if (fcol(row,col) == 1 && (min=~(max=0))) continue;
|
|
+// pix = image + row*width + col;
|
|
+// hex = allhex[row % 3][col % 3][0];
|
|
+// if (!max) FORC(6) {
|
|
+// val = pix[hex[c]][1];
|
|
+// if (min > val) min = val;
|
|
+// if (max < val) max = val;
|
|
+// }
|
|
+// pix[0][1] = min;
|
|
+// pix[0][3] = max;
|
|
+// switch ((row-sgrow) % 3) {
|
|
+// case 1: if (row < height-3) { row++; col--; } break;
|
|
+// case 2: if ((min=~(max=0)) && (col+=2) < width-3 && row > 2) row--;
|
|
+// }
|
|
+// }
|
|
+//
|
|
+// for (top=3; top < height-19; top += TS-16)
|
|
+// for (left=3; left < width-19; left += TS-16) {
|
|
+// mrow = MIN (top+TS, height-3);
|
|
+// mcol = MIN (left+TS, width-3);
|
|
+// for (row=top; row < mrow; row++)
|
|
+// for (col=left; col < mcol; col++)
|
|
+// memcpy (rgb[0][row-top][col-left], image[row*width+col], 6);
|
|
+// FORC3 memcpy (rgb[c+1], rgb[0], sizeof *rgb);
|
|
+//
|
|
+///* Interpolate green horizontally, vertically, and along both diagonals: */
|
|
+// for (row=top; row < mrow; row++)
|
|
+// for (col=left; col < mcol; col++) {
|
|
+// if ((f = fcol(row,col)) == 1) continue;
|
|
+// pix = image + row*width + col;
|
|
+// hex = allhex[row % 3][col % 3][0];
|
|
+// color[1][0] = 174 * (pix[ hex[1]][1] + pix[ hex[0]][1]) -
|
|
+// 46 * (pix[2*hex[1]][1] + pix[2*hex[0]][1]);
|
|
+// color[1][1] = 223 * pix[ hex[3]][1] + pix[ hex[2]][1] * 33 +
|
|
+// 92 * (pix[ 0 ][f] - pix[ -hex[2]][f]);
|
|
+// FORC(2) color[1][2+c] =
|
|
+// 164 * pix[hex[4+c]][1] + 92 * pix[-2*hex[4+c]][1] + 33 *
|
|
+// (2*pix[0][f] - pix[3*hex[4+c]][f] - pix[-3*hex[4+c]][f]);
|
|
+// FORC4 rgb[c^!((row-sgrow) % 3)][row-top][col-left][1] =
|
|
+// LIM(color[1][c] >> 8,pix[0][1],pix[0][3]);
|
|
+// }
|
|
+//
|
|
+// for (pass=0; pass < passes; pass++) {
|
|
+// if (pass == 1)
|
|
+// memcpy (rgb+=4, buffer, 4*sizeof *rgb);
|
|
+//
|
|
+///* Recalculate green from interpolated values of closer pixels: */
|
|
+// if (pass) {
|
|
+// for (row=top+2; row < mrow-2; row++)
|
|
+// for (col=left+2; col < mcol-2; col++) {
|
|
+// if ((f = fcol(row,col)) == 1) continue;
|
|
+// pix = image + row*width + col;
|
|
+// hex = allhex[row % 3][col % 3][1];
|
|
+// for (d=3; d < 6; d++) {
|
|
+// rix = &rgb[(d-2)^!((row-sgrow) % 3)][row-top][col-left];
|
|
+// val = rix[-2*hex[d]][1] + 2*rix[hex[d]][1]
|
|
+// - rix[-2*hex[d]][f] - 2*rix[hex[d]][f] + 3*rix[0][f];
|
|
+// rix[0][1] = LIM(val/3,pix[0][1],pix[0][3]);
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+//
|
|
+///* Interpolate red and blue values for solitary green pixels: */
|
|
+// for (row=(top-sgrow+4)/3*3+sgrow; row < mrow-2; row+=3)
|
|
+// for (col=(left-sgcol+4)/3*3+sgcol; col < mcol-2; col+=3) {
|
|
+// rix = &rgb[0][row-top][col-left];
|
|
+// h = fcol(row,col+1);
|
|
+// memset (diff, 0, sizeof diff);
|
|
+// for (i=1, d=0; d < 6; d++, i^=TS^1, h^=2) {
|
|
+// for (c=0; c < 2; c++, h^=2) {
|
|
+// g = 2*rix[0][1] - rix[i<<c][1] - rix[-i<<c][1];
|
|
+// color[h][d] = g + rix[i<<c][h] + rix[-i<<c][h];
|
|
+// if (d > 1)
|
|
+// diff[d] += SQR (rix[i<<c][1] - rix[-i<<c][1]
|
|
+// - rix[i<<c][h] + rix[-i<<c][h]) + SQR(g);
|
|
+// }
|
|
+// if (d > 1 && (d & 1))
|
|
+// if (diff[d-1] < diff[d])
|
|
+// FORC(2) color[c*2][d] = color[c*2][d-1];
|
|
+// if (d < 2 || (d & 1)) {
|
|
+// FORC(2) rix[0][c*2] = CLIP(color[c*2][d]/2);
|
|
+// rix += TS*TS;
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+//
|
|
+///* Interpolate red for blue pixels and vice versa: */
|
|
+// for (row=top+3; row < mrow-3; row++)
|
|
+// for (col=left+3; col < mcol-3; col++) {
|
|
+// if ((f = 2-fcol(row,col)) == 1) continue;
|
|
+// rix = &rgb[0][row-top][col-left];
|
|
+// c = (row-sgrow) % 3 ? TS:1;
|
|
+// h = 3 * (c ^ TS ^ 1);
|
|
+// for (d=0; d < 4; d++, rix += TS*TS) {
|
|
+// i = d > 1 || ((d ^ c) & 1) ||
|
|
+// ((ABS(rix[0][1]-rix[c][1])+ABS(rix[0][1]-rix[-c][1])) <
|
|
+// 2*(ABS(rix[0][1]-rix[h][1])+ABS(rix[0][1]-rix[-h][1]))) ? c:h;
|
|
+// rix[0][f] = CLIP((rix[i][f] + rix[-i][f] +
|
|
+// 2*rix[0][1] - rix[i][1] - rix[-i][1])/2);
|
|
+// }
|
|
+// }
|
|
+//
|
|
+///* Fill in red and blue for 2x2 blocks of green: */
|
|
+// for (row=top+2; row < mrow-2; row++) if ((row-sgrow) % 3)
|
|
+// for (col=left+2; col < mcol-2; col++) if ((col-sgcol) % 3) {
|
|
+// rix = &rgb[0][row-top][col-left];
|
|
+// hex = allhex[row % 3][col % 3][1];
|
|
+// for (d=0; d < ndir; d+=2, rix += TS*TS)
|
|
+// if (hex[d] + hex[d+1]) {
|
|
+// g = 3*rix[0][1] - 2*rix[hex[d]][1] - rix[hex[d+1]][1];
|
|
+// for (c=0; c < 4; c+=2) rix[0][c] =
|
|
+// CLIP((g + 2*rix[hex[d]][c] + rix[hex[d+1]][c])/3);
|
|
+// } else {
|
|
+// g = 2*rix[0][1] - rix[hex[d]][1] - rix[hex[d+1]][1];
|
|
+// for (c=0; c < 4; c+=2) rix[0][c] =
|
|
+// CLIP((g + rix[hex[d]][c] + rix[hex[d+1]][c])/2);
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+// rgb = (ushort(*)[TS][TS][3]) buffer;
|
|
+// mrow -= top;
|
|
+// mcol -= left;
|
|
+//
|
|
+///* Convert to CIELab and differentiate in all directions: */
|
|
+// for (d=0; d < ndir; d++) {
|
|
+// for (row=2; row < mrow-2; row++)
|
|
+// for (col=2; col < mcol-2; col++)
|
|
+// cielab (rgb[d][row][col], lab[row][col]);
|
|
+// for (f=dir[d & 3],row=3; row < mrow-3; row++)
|
|
+// for (col=3; col < mcol-3; col++) {
|
|
+// lix = &lab[row][col];
|
|
+// g = 2*lix[0][0] - lix[f][0] - lix[-f][0];
|
|
+// drv[d][row][col] = SQR(g)
|
|
+// + SQR((2*lix[0][1] - lix[f][1] - lix[-f][1] + g*500/232))
|
|
+// + SQR((2*lix[0][2] - lix[f][2] - lix[-f][2] - g*500/580));
|
|
+// }
|
|
+// }
|
|
+//
|
|
+///* Build homogeneity maps from the derivatives: */
|
|
+// memset(homo, 0, ndir*TS*TS);
|
|
+// for (row=4; row < mrow-4; row++)
|
|
+// for (col=4; col < mcol-4; col++) {
|
|
+// for (tr=FLT_MAX, d=0; d < ndir; d++)
|
|
+// if (tr > drv[d][row][col])
|
|
+// tr = drv[d][row][col];
|
|
+// tr *= 8;
|
|
+// for (d=0; d < ndir; d++)
|
|
+// for (v=-1; v <= 1; v++)
|
|
+// for (h=-1; h <= 1; h++)
|
|
+// if (drv[d][row+v][col+h] <= tr)
|
|
+// homo[d][row][col]++;
|
|
+// }
|
|
+//
|
|
+///* Average the most homogenous pixels for the final result: */
|
|
+// if (height-top < TS+4) mrow = height-top+2;
|
|
+// if (width-left < TS+4) mcol = width-left+2;
|
|
+// for (row = MIN(top,8); row < mrow-8; row++)
|
|
+// for (col = MIN(left,8); col < mcol-8; col++) {
|
|
+// for (d=0; d < ndir; d++)
|
|
+// for (hm[d]=0, v=-2; v <= 2; v++)
|
|
+// for (h=-2; h <= 2; h++)
|
|
+// hm[d] += homo[d][row+v][col+h];
|
|
+// for (d=0; d < ndir-4; d++)
|
|
+// if (hm[d] < hm[d+4]) hm[d ] = 0; else
|
|
+// if (hm[d] > hm[d+4]) hm[d+4] = 0;
|
|
+// for (max=hm[0],d=1; d < ndir; d++)
|
|
+// if (max < hm[d]) max = hm[d];
|
|
+// max -= max >> 3;
|
|
+// memset (avg, 0, sizeof avg);
|
|
+// for (d=0; d < ndir; d++)
|
|
+// if (hm[d] >= max) {
|
|
+// FORC3 avg[c] += rgb[d][row][col][c];
|
|
+// avg[3]++;
|
|
+// }
|
|
+// FORC3 image[(row+top)*width+col+left][c] = avg[c]/avg[3];
|
|
+// }
|
|
+// }
|
|
+// free(buffer);
|
|
+// border_interpolate(8);
|
|
+//}
|
|
+//#undef fcol
|
|
+//
|
|
+//
|
|
+//#undef TS
|
|
+
|
|
+//void CLASS median_filter()
|
|
+//{
|
|
+// ushort (*pix)[4];
|
|
+// int pass, c, i, j, k, med[9];
|
|
+// static const uchar opt[] = /* Optimal 9-element median search */
|
|
+// { 1,2, 4,5, 7,8, 0,1, 3,4, 6,7, 1,2, 4,5, 7,8,
|
|
+// 0,3, 5,8, 4,7, 3,6, 1,4, 2,5, 4,7, 4,2, 6,4, 4,2 };
|
|
+//
|
|
+// for (pass=1; pass <= med_passes; pass++) {
|
|
+// if (verbose)
|
|
+// fprintf (stderr,_("Median filter pass %d...\n"), pass);
|
|
+// for (c=0; c < 3; c+=2) {
|
|
+// for (pix = image; pix < image+width*height; pix++)
|
|
+// pix[0][3] = pix[0][c];
|
|
+// for (pix = image+width; pix < image+width*(height-1); pix++) {
|
|
+// if ((pix-image+1) % width < 2) continue;
|
|
+// for (k=0, i = -width; i <= width; i += width)
|
|
+// for (j = i-1; j <= i+1; j++)
|
|
+// med[k++] = pix[j][3] - pix[j][1];
|
|
+// for (i=0; i < sizeof opt; i+=2)
|
|
+// if (med[opt[i]] > med[opt[i+1]])
|
|
+// SWAP (med[opt[i]] , med[opt[i+1]]);
|
|
+// pix[0][c] = CLIP(med[4] + pix[0][1]);
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+//}
|
|
+//
|
|
+//void CLASS blend_highlights()
|
|
+//{
|
|
+// int clip=INT_MAX, row, col, c, i, j;
|
|
+// static const float trans[2][4][4] =
|
|
+// { { { 1,1,1 }, { 1.7320508,-1.7320508,0 }, { -1,-1,2 } },
|
|
+// { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } };
|
|
+// static const float itrans[2][4][4] =
|
|
+// { { { 1,0.8660254,-0.5 }, { 1,-0.8660254,-0.5 }, { 1,0,1 } },
|
|
+// { { 1,1,1,1 }, { 1,-1,1,-1 }, { 1,1,-1,-1 }, { 1,-1,-1,1 } } };
|
|
+// float cam[2][4], lab[2][4], sum[2], chratio;
|
|
+//
|
|
+// if ((unsigned) (colors-3) > 1) return;
|
|
+// if (verbose) fprintf (stderr,_("Blending highlights...\n"));
|
|
+// FORCC if (clip > (i = 65535*pre_mul[c])) clip = i;
|
|
+// for (row=0; row < height; row++)
|
|
+// for (col=0; col < width; col++) {
|
|
+// FORCC if (image[row*width+col][c] > clip) break;
|
|
+// if (c == colors) continue;
|
|
+// FORCC {
|
|
+// cam[0][c] = image[row*width+col][c];
|
|
+// cam[1][c] = MIN(cam[0][c],clip);
|
|
+// }
|
|
+// for (i=0; i < 2; i++) {
|
|
+// FORCC for (lab[i][c]=j=0; j < colors; j++)
|
|
+// lab[i][c] += trans[colors-3][c][j] * cam[i][j];
|
|
+// for (sum[i]=0,c=1; c < colors; c++)
|
|
+// sum[i] += SQR(lab[i][c]);
|
|
+// }
|
|
+// chratio = sqrt(sum[1]/sum[0]);
|
|
+// for (c=1; c < colors; c++)
|
|
+// lab[0][c] *= chratio;
|
|
+// FORCC for (cam[0][c]=j=0; j < colors; j++)
|
|
+// cam[0][c] += itrans[colors-3][c][j] * lab[0][j];
|
|
+// FORCC image[row*width+col][c] = cam[0][c] / colors;
|
|
+// }
|
|
+//}
|
|
+//
|
|
+//#define SCALE (4 >> shrink)
|
|
+//void CLASS recover_highlights()
|
|
+//{
|
|
+// float *map, sum, wgt, grow;
|
|
+// int hsat[4], count, spread, change, val, i;
|
|
+// unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x;
|
|
+// ushort *pixel;
|
|
+// static const signed char dir[8][2] =
|
|
+// { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} };
|
|
+//
|
|
+// if (verbose) fprintf (stderr,_("Rebuilding highlights...\n"));
|
|
+//
|
|
+// grow = pow (2, 4-highlight);
|
|
+// FORCC hsat[c] = 32000 * pre_mul[c];
|
|
+// for (kc=0, c=1; c < colors; c++)
|
|
+// if (pre_mul[kc] < pre_mul[c]) kc = c;
|
|
+// high = height / SCALE;
|
|
+// wide = width / SCALE;
|
|
+// map = (float *) calloc (high, wide*sizeof *map);
|
|
+// merror (map, "recover_highlights()");
|
|
+// FORCC if (c != kc) {
|
|
+// memset (map, 0, high*wide*sizeof *map);
|
|
+// for (mrow=0; mrow < high; mrow++)
|
|
+// for (mcol=0; mcol < wide; mcol++) {
|
|
+// sum = wgt = count = 0;
|
|
+// for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++)
|
|
+// for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) {
|
|
+// pixel = image[row*width+col];
|
|
+// if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000) {
|
|
+// sum += pixel[c];
|
|
+// wgt += pixel[kc];
|
|
+// count++;
|
|
+// }
|
|
+// }
|
|
+// if (count == SCALE*SCALE)
|
|
+// map[mrow*wide+mcol] = sum / wgt;
|
|
+// }
|
|
+// for (spread = 32/grow; spread--; ) {
|
|
+// for (mrow=0; mrow < high; mrow++)
|
|
+// for (mcol=0; mcol < wide; mcol++) {
|
|
+// if (map[mrow*wide+mcol]) continue;
|
|
+// sum = count = 0;
|
|
+// for (d=0; d < 8; d++) {
|
|
+// y = mrow + dir[d][0];
|
|
+// x = mcol + dir[d][1];
|
|
+// if (y < high && x < wide && map[y*wide+x] > 0) {
|
|
+// sum += (1 + (d & 1)) * map[y*wide+x];
|
|
+// count += 1 + (d & 1);
|
|
+// }
|
|
+// }
|
|
+// if (count > 3)
|
|
+// map[mrow*wide+mcol] = - (sum+grow) / (count+grow);
|
|
+// }
|
|
+// for (change=i=0; i < high*wide; i++)
|
|
+// if (map[i] < 0) {
|
|
+// map[i] = -map[i];
|
|
+// change = 1;
|
|
+// }
|
|
+// if (!change) break;
|
|
+// }
|
|
+// for (i=0; i < high*wide; i++)
|
|
+// if (map[i] == 0) map[i] = 1;
|
|
+// for (mrow=0; mrow < high; mrow++)
|
|
+// for (mcol=0; mcol < wide; mcol++) {
|
|
+// for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++)
|
|
+// for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) {
|
|
+// pixel = image[row*width+col];
|
|
+// if (pixel[c] / hsat[c] > 1) {
|
|
+// val = pixel[kc] * map[mrow*wide+mcol];
|
|
+// if (pixel[c] < val) pixel[c] = CLIP(val);
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+// free (map);
|
|
+//}
|
|
+//#undef SCALE
|
|
|
|
void CLASS tiff_get (unsigned base,
|
|
unsigned *tag, unsigned *type, unsigned *len, unsigned *save)
|
|
@@ -5139,7 +5127,7 @@
|
|
}
|
|
}
|
|
|
|
-int CLASS parse_tiff_ifd (int base);
|
|
+/*RT int CLASS parse_tiff_ifd (int base);*/
|
|
|
|
void CLASS parse_makernote (int base, int uptag)
|
|
{
|
|
@@ -5244,6 +5232,11 @@
|
|
tag |= uptag << 16;
|
|
if (tag == 2 && strstr(make,"NIKON") && !iso_speed)
|
|
iso_speed = (get2(),get2());
|
|
+ if ((tag == 0x25 || tag == 0x28) && strstr(make,"NIKON") && !iso_speed) { // Nikon ISOInfo Tags/ISO & ISO2
|
|
+ uchar iso[1];
|
|
+ fread (iso, 1, 1, ifp);
|
|
+ iso_speed = 100 * pow(2,(float)iso[0]/12.0-5);
|
|
+ }
|
|
if (tag == 4 && len > 26 && len < 35) {
|
|
if ((i=(get4(),get2())) != 0x7fff && !iso_speed)
|
|
iso_speed = 50 * pow (2, i/32.0 - 4);
|
|
@@ -5296,12 +5289,16 @@
|
|
cam_mul[2] = get4() << 2;
|
|
}
|
|
}
|
|
- if (tag == 0x15 && type == 2 && is_raw)
|
|
+ //if (tag == 0x15 && type == 2 && is_raw)
|
|
+ if (tag == 0x15 && type == 2 && is_raw && strstr(model, "Hasselblad ") != model) // RT: don't overwrite already parsed Hasselblad model
|
|
fread (model, 64, 1, ifp);
|
|
if (strstr(make,"PENTAX")) {
|
|
if (tag == 0x1b) tag = 0x1018;
|
|
if (tag == 0x1c) tag = 0x1017;
|
|
}
|
|
+ if (tag == 0x19 && !strcmp(make,"Hasselblad") && len == 0x300000) { // RT
|
|
+ parse_hasselblad_gain(); // RT
|
|
+ } // RT
|
|
if (tag == 0x1d)
|
|
while ((c = fgetc(ifp)) && c != EOF)
|
|
serial = serial*10 + (isdigit(c) ? c - '0' : c % 10);
|
|
@@ -5491,14 +5488,14 @@
|
|
while (entries--) {
|
|
tiff_get (base, &tag, &type, &len, &save);
|
|
switch (tag) {
|
|
- case 33434: tiff_ifd[tiff_nifds-1].shutter =
|
|
- shutter = getreal(type); break;
|
|
+ case 33434: tiff_ifd[tiff_nifds-1].shutter = shutter = getreal(type); break;
|
|
case 33437: aperture = getreal(type); break;
|
|
case 34855: iso_speed = get2(); break;
|
|
+ case 34866: if((!iso_speed) || iso_speed == 65535) iso_speed = get4();break;
|
|
case 36867:
|
|
case 36868: get_timestamp(0); break;
|
|
case 37377: if ((expo = -getreal(type)) < 128)
|
|
- tiff_ifd[tiff_nifds-1].shutter =
|
|
+ tiff_ifd[tiff_nifds-1].shutter =
|
|
shutter = pow (2, expo); break;
|
|
case 37378: aperture = pow (2, getreal(type)/2); break;
|
|
case 37386: focal_len = getreal(type); break;
|
|
@@ -5667,28 +5664,33 @@
|
|
}
|
|
}
|
|
|
|
-void CLASS parse_minolta (int base);
|
|
-int CLASS parse_tiff (int base);
|
|
+/*RT void CLASS parse_minolta (int base); */
|
|
+/*RT int CLASS parse_tiff (int base);*/
|
|
|
|
int CLASS parse_tiff_ifd (int base)
|
|
{
|
|
unsigned entries, tag, type, len, plen=16, save;
|
|
- int ifd, use_cm=0, cfa, i, j, c, ima_len=0;
|
|
+ int ifd, use_cm=0, cfa, i, j, c, ima_len=0,cm_D65=0;
|
|
char software[64], *cbuf, *cp;
|
|
uchar cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256];
|
|
- double cc[4][4], cm[4][3], cam_xyz[4][3], num;
|
|
+ double cc[2][4][4];
|
|
+ double cm[2][4][3] = {NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN};
|
|
+ double cam_xyz[4][3], num;
|
|
double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 };
|
|
unsigned sony_curve[] = { 0,0,0,0,0,4095 };
|
|
unsigned *buf, sony_offset=0, sony_length=0, sony_key=0;
|
|
struct jhead jh;
|
|
- FILE *sfp;
|
|
+/*RT*/ IMFILE *sfp;
|
|
|
|
if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0])
|
|
return 1;
|
|
ifd = tiff_nifds++;
|
|
for (j=0; j < 4; j++)
|
|
for (i=0; i < 4; i++)
|
|
- cc[j][i] = i == j;
|
|
+ {
|
|
+ cc[0][j][i] = i == j;
|
|
+ cc[1][j][i] = i == j;
|
|
+ }
|
|
entries = get2();
|
|
if (entries > 512) return 1;
|
|
while (entries--) {
|
|
@@ -5758,7 +5760,8 @@
|
|
fgets (make, 64, ifp);
|
|
break;
|
|
case 272: /* Model */
|
|
- fgets (model, 64, ifp);
|
|
+ if (strstr(model, "Hasselblad ") != model) // RT: if Hasselblad, only parse the first model name (otherwise it can change from say Hasselblad CFV-50 to Hasselblad CW (ie the camera body model, not the back model which we are interested in)
|
|
+ fgets (model, 64, ifp);
|
|
break;
|
|
case 280: /* Panasonic RW2 offset */
|
|
if (type != 4) break;
|
|
@@ -5818,6 +5821,9 @@
|
|
case 315: /* Artist */
|
|
fread (artist, 64, 1, ifp);
|
|
break;
|
|
+ case 317: /* Predictor */
|
|
+ tiff_ifd[ifd].predictor = getint(type);
|
|
+ break;
|
|
case 322: /* TileWidth */
|
|
tiff_ifd[ifd].tile_width = getint(type);
|
|
break;
|
|
@@ -5833,6 +5839,9 @@
|
|
is_raw = 5;
|
|
}
|
|
break;
|
|
+ case 325: /* TileByteCounts */
|
|
+ tiff_ifd[ifd].bytes = len > 1 ? ftell(ifp) : get4();
|
|
+ break;
|
|
case 330: /* SubIFDs */
|
|
if (!strcmp(model,"DSLR-A100") && tiff_ifd[ifd].width == 3872) {
|
|
load_raw = &CLASS sony_arw_load_raw;
|
|
@@ -5846,6 +5855,9 @@
|
|
fseek (ifp, i+4, SEEK_SET);
|
|
}
|
|
break;
|
|
+ case 339:
|
|
+ tiff_ifd[ifd].sample_format = getint(type);
|
|
+ break;
|
|
case 400:
|
|
strcpy (make, "Sarnoff");
|
|
maximum = 0xfff;
|
|
@@ -6063,12 +6075,21 @@
|
|
case 61450:
|
|
cblack[4] = cblack[5] = MIN(sqrt(len),64);
|
|
case 50714: /* BlackLevel */
|
|
- if (!(cblack[4] * cblack[5]))
|
|
- cblack[4] = cblack[5] = 1;
|
|
- FORC (cblack[4] * cblack[5])
|
|
- cblack[6+c] = getreal(type);
|
|
- black = 0;
|
|
- break;
|
|
+ if(cblack[4] * cblack[5] == 0) {
|
|
+ int dblack[] = { 0,0,0,0 };
|
|
+ black = getreal(type);
|
|
+ if ((unsigned)(filters+1) < 1000) break;
|
|
+ dblack[0] = dblack[1] = dblack[2] = dblack[3] = black;
|
|
+ if (colors == 3)
|
|
+ filters |= ((filters >> 2 & 0x22222222) |
|
|
+ (filters << 2 & 0x88888888)) & filters << 1;
|
|
+ FORC4 cblack[filters >> (c << 1) & 3] = dblack[c];
|
|
+ } else {
|
|
+ FORC (cblack[4] * cblack[5])
|
|
+ cblack[6+c] = getreal(type);
|
|
+ }
|
|
+ black = 0;
|
|
+ break;
|
|
case 50715: /* BlackLevelDeltaH */
|
|
case 50716: /* BlackLevelDeltaV */
|
|
for (num=i=0; i < (len & 0xffff); i++)
|
|
@@ -6085,13 +6106,13 @@
|
|
case 50721: /* ColorMatrix1 */
|
|
case 50722: /* ColorMatrix2 */
|
|
FORCC for (j=0; j < 3; j++)
|
|
- cm[c][j] = getreal(type);
|
|
+ cm[tag-50721][c][j] = getreal(type);
|
|
use_cm = 1;
|
|
break;
|
|
case 50723: /* CameraCalibration1 */
|
|
case 50724: /* CameraCalibration2 */
|
|
for (i=0; i < colors; i++)
|
|
- FORCC cc[i][c] = getreal(type);
|
|
+ FORCC cc[tag-50723][i][c] = getreal(type);
|
|
break;
|
|
case 50727: /* AnalogBalance */
|
|
FORCC ab[c] = getreal(type);
|
|
@@ -6114,6 +6135,11 @@
|
|
case 50752:
|
|
read_shorts (cr2_slice, 3);
|
|
break;
|
|
+ case 50778:
|
|
+ case 50779:
|
|
+ if( get2() == 21 )
|
|
+ cm_D65 = (tag-50778);
|
|
+ break;
|
|
case 50829: /* ActiveArea */
|
|
top_margin = getint(type);
|
|
left_margin = getint(type);
|
|
@@ -6146,21 +6172,27 @@
|
|
fread (buf, sony_length, 1, ifp);
|
|
sony_decrypt (buf, sony_length/4, 1, sony_key);
|
|
sfp = ifp;
|
|
- if ((ifp = tmpfile())) {
|
|
- fwrite (buf, sony_length, 1, ifp);
|
|
- fseek (ifp, 0, SEEK_SET);
|
|
- parse_tiff_ifd (-sony_offset);
|
|
- fclose (ifp);
|
|
- }
|
|
+/*RT*/ ifp = fopen (buf, sony_length);
|
|
+// if ((ifp = tmpfile())) {
|
|
+// fwrite (buf, sony_length, 1, ifp);
|
|
+// fseek (ifp, 0, SEEK_SET);
|
|
+ parse_tiff_ifd (-sony_offset);
|
|
+// fclose (ifp);
|
|
+// }
|
|
+ if(ifp)
|
|
+ fclose(ifp);
|
|
ifp = sfp;
|
|
free (buf);
|
|
}
|
|
+
|
|
for (i=0; i < colors; i++)
|
|
- FORCC cc[i][c] *= ab[i];
|
|
+ FORCC cc[cm_D65][i][c] *= ab[i];
|
|
if (use_cm) {
|
|
+ if(cm_D65 == 1 && std::isnan(cm[1][0][0]))
|
|
+ cm_D65 = 0;
|
|
FORCC for (i=0; i < 3; i++)
|
|
for (cam_xyz[c][i]=j=0; j < colors; j++)
|
|
- cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i];
|
|
+ cam_xyz[c][i] += cc[cm_D65][c][j] * cm[cm_D65][j][i] * xyz[i];
|
|
cam_xyz_coeff (cmatrix, cam_xyz);
|
|
}
|
|
if (asn[0]) {
|
|
@@ -6168,13 +6200,14 @@
|
|
FORCC cam_mul[c] = 1 / asn[c];
|
|
}
|
|
if (!use_cm)
|
|
- FORCC pre_mul[c] /= cc[c][c];
|
|
+ FORCC pre_mul[c] /= cc[cm_D65][c][c];
|
|
return 0;
|
|
}
|
|
|
|
int CLASS parse_tiff (int base)
|
|
{
|
|
int doff;
|
|
+ /*RT*/ if (exif_base == -1) exif_base = base;
|
|
|
|
fseek (ifp, base, SEEK_SET);
|
|
order = get2();
|
|
@@ -6206,6 +6239,7 @@
|
|
shutter = tiff_ifd[i].shutter;
|
|
tiff_ifd[i].shutter = shutter;
|
|
}
|
|
+
|
|
for (i=0; i < tiff_nifds; i++) {
|
|
if (max_samp < tiff_ifd[i].samples)
|
|
max_samp = tiff_ifd[i].samples;
|
|
@@ -6266,7 +6300,12 @@
|
|
case 8: load_raw = &CLASS eight_bit_load_raw; break;
|
|
case 12: if (tiff_ifd[raw].phint == 2)
|
|
load_flags = 6;
|
|
- load_raw = &CLASS packed_load_raw; break;
|
|
+ if(!strncmp(make,"SONY",4) && tiff_ifd[raw].comp == 1) { // hack for some sony cameras which report as 12 bit uncompressed but in fact are 14 bit uncompressed
|
|
+ tiff_bps = 14;
|
|
+ } else {
|
|
+ load_raw = &CLASS packed_load_raw;
|
|
+ break;
|
|
+ }
|
|
case 14: load_flags = 0;
|
|
case 16: load_raw = &CLASS unpacked_load_raw;
|
|
if (!strncmp(make,"OLYMPUS",7) &&
|
|
@@ -6305,6 +6344,7 @@
|
|
case 32803: load_raw = &CLASS kodak_65000_load_raw;
|
|
}
|
|
case 32867: case 34892: break;
|
|
+ case 8: break;
|
|
default: is_raw = 0;
|
|
}
|
|
if (!dng_version)
|
|
@@ -6390,7 +6430,7 @@
|
|
{
|
|
const char *file, *ext;
|
|
char *jname, *jfile, *jext;
|
|
- FILE *save=ifp;
|
|
+/*RT*/ IMFILE *save=ifp;
|
|
|
|
ext = strrchr (ifname, '.');
|
|
file = strrchr (ifname, '/');
|
|
@@ -6412,13 +6452,14 @@
|
|
} else
|
|
while (isdigit(*--jext)) {
|
|
if (*jext != '9') {
|
|
- (*jext)++;
|
|
+ (*jext)++;
|
|
break;
|
|
}
|
|
*jext = '0';
|
|
}
|
|
if (strcmp (jname, ifname)) {
|
|
- if ((ifp = fopen (jname, "rb"))) {
|
|
+/*RT*/ if ((ifp = fopen (jname))) {
|
|
+// if ((ifp = fopen (jname, "rb"))) {
|
|
if (verbose)
|
|
fprintf (stderr,_("Reading metadata from %s ...\n"), jname);
|
|
parse_tiff (12);
|
|
@@ -6693,6 +6734,7 @@
|
|
load_raw = ph1.format < 3 ?
|
|
&CLASS phase_one_load_raw : &CLASS phase_one_load_raw_c;
|
|
maximum = 0xffff;
|
|
+ tiff_bps = 16;
|
|
strcpy (make, "Phase One");
|
|
if (model[0]) return;
|
|
switch (raw_height) {
|
|
@@ -6761,7 +6803,11 @@
|
|
order = get2();
|
|
hlen = get4();
|
|
if (get4() == 0x48454150) /* "HEAP" */
|
|
+/*RT*/ {
|
|
+/*RT*/ ciff_base = save+hlen;
|
|
+/*RT*/ ciff_len = len-hlen;
|
|
parse_ciff (save+hlen, len-hlen, 0);
|
|
+/*RT*/ }
|
|
if (parse_tiff (save+6)) apply_tiff();
|
|
fseek (ifp, save+len, SEEK_SET);
|
|
}
|
|
@@ -7033,7 +7079,8 @@
|
|
{
|
|
static const struct {
|
|
const char *prefix;
|
|
- short black, maximum, trans[12];
|
|
+ unsigned short black, maximum; // RT: Change to UShort
|
|
+ short trans[12];
|
|
} table[] = {
|
|
{ "AgfaPhoto DC-833m", 0, 0, /* DJC */
|
|
{ 11438,-3762,-1115,-2409,9914,2497,-1227,2295,5300 } },
|
|
@@ -7977,12 +8024,12 @@
|
|
{ 6596,-2079,-562,-4782,13016,1933,-970,1581,5181 } },
|
|
{ "Sony DSC-RX100", 0, 0,
|
|
{ 8651,-2754,-1057,-3464,12207,1373,-568,1398,4434 } },
|
|
- { "Sony DSC-RX10", 0, 0, /* also RX10M2 */
|
|
+ { "Sony DSC-RX10", 0, 0,
|
|
{ 6679,-1825,-745,-5047,13256,1953,-1580,2422,5183 } },
|
|
{ "Sony DSC-RX1RM2", 0, 0,
|
|
{ 6629,-1900,-483,-4618,12349,2550,-622,1381,6514 } },
|
|
{ "Sony DSC-RX1", 0, 0,
|
|
- { 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } },
|
|
+ { 6344,-1612,-462,-4863,12477,2681,-865,1786,6899 } },
|
|
{ "Sony DSLR-A100", 0, 0xfeb,
|
|
{ 9437,-2811,-774,-8405,16215,2290,-710,596,7181 } },
|
|
{ "Sony DSLR-A290", 0, 0,
|
|
@@ -8088,6 +8135,33 @@
|
|
}
|
|
break;
|
|
}
|
|
+ if (load_raw == &CLASS sony_arw2_load_raw) { // RT: arw2 scale fix
|
|
+ black <<= 2;
|
|
+ tiff_bps += 2;
|
|
+ }
|
|
+ { /* Check for RawTherapee table overrides and extensions */
|
|
+ int black_level, white_level;
|
|
+ short trans[12];
|
|
+ if (dcraw_coeff_overrides(make, model, iso_speed, trans, &black_level, &white_level)) {
|
|
+ if (black_level > -1) {
|
|
+ black = (ushort)black_level;
|
|
+ }
|
|
+ if (white_level > -1) {
|
|
+ maximum = (ushort)white_level;
|
|
+ if(tiff_bps > 0) {
|
|
+ unsigned compare = ((uint64_t)1 << tiff_bps) - 1; // use uint64_t to avoid overflow if tiff_bps == 32
|
|
+ while(maximum > compare)
|
|
+ maximum >>= 1;
|
|
+ }
|
|
+ }
|
|
+ if (trans[0]) {
|
|
+ for (j=0; j < 12; j++) {
|
|
+ ((double *)cam_xyz)[j] = trans[j] / 10000.0;
|
|
+ }
|
|
+ cam_xyz_coeff (rgb_cam,cam_xyz);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
void CLASS simple_coeff (int index)
|
|
@@ -8410,7 +8484,7 @@
|
|
tiff_flip = flip = filters = UINT_MAX; /* unknown */
|
|
raw_height = raw_width = fuji_width = fuji_layout = cr2_slice[0] = 0;
|
|
maximum = height = width = top_margin = left_margin = 0;
|
|
- cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = 0;
|
|
+ cdesc[0] = desc[0] = artist[0] = make[0] = model[0] = model2[0] = model3[0] = 0;
|
|
iso_speed = shutter = aperture = focal_len = unique_id = 0;
|
|
tiff_nifds = 0;
|
|
memset (tiff_ifd, 0, sizeof tiff_ifd);
|
|
@@ -8442,13 +8516,20 @@
|
|
fread (head, 1, 32, ifp);
|
|
fseek (ifp, 0, SEEK_END);
|
|
flen = fsize = ftell(ifp);
|
|
- if ((cp = (char *) memmem (head, 32, "MMMM", 4)) ||
|
|
- (cp = (char *) memmem (head, 32, "IIII", 4))) {
|
|
+ /*RT*/ if (fsize<100000) {
|
|
+ is_raw = 0;
|
|
+ return;
|
|
+ }
|
|
+ /* RT: changed string constant */
|
|
+ if ((cp = (char *) memmem (head, 32, (char*)"MMMM", 4)) ||
|
|
+ (cp = (char *) memmem (head, 32, (char*)"IIII", 4))) {
|
|
parse_phase_one (cp-head);
|
|
if (cp-head && parse_tiff(0)) apply_tiff();
|
|
} else if (order == 0x4949 || order == 0x4d4d) {
|
|
if (!memcmp (head+6,"HEAPCCDR",8)) {
|
|
data_offset = hlen;
|
|
+/*RT*/ ciff_base = hlen;
|
|
+/*RT*/ ciff_len = fsize - hlen;
|
|
parse_ciff (hlen, flen-hlen, 0);
|
|
load_raw = &CLASS canon_load_raw;
|
|
} else if (parse_tiff(0)) apply_tiff();
|
|
@@ -8491,9 +8572,10 @@
|
|
parse_fuji (i);
|
|
}
|
|
load_raw = &CLASS unpacked_load_raw;
|
|
- fseek (ifp, 100+28*(shot_select > 0), SEEK_SET);
|
|
+ fseek (ifp, 100+28*(shot_select > 0 && shot_select < is_raw), SEEK_SET);
|
|
parse_tiff (data_offset = get4());
|
|
parse_tiff (thumb_offset+12);
|
|
+/*RT*/ exif_base = thumb_offset+12;
|
|
apply_tiff();
|
|
} else if (!memcmp (head,"RIFF",4)) {
|
|
fseek (ifp, 0, SEEK_SET);
|
|
@@ -8607,9 +8689,10 @@
|
|
if (make[0] == 0) parse_smal (0, flen);
|
|
if (make[0] == 0) {
|
|
parse_jpeg(0);
|
|
- if (!(strncmp(model,"ov",2) && strncmp(model,"RP_OV",5)) &&
|
|
- !fseek (ifp, -6404096, SEEK_END) &&
|
|
- fread (head, 1, 32, ifp) && !strcmp(head,"BRCMn")) {
|
|
+ //RT fix for the use of fseek below
|
|
+ if (!(strncmp(model,"ov",2) && strncmp(model,"RP_OV",5))) {
|
|
+ fseek (ifp, -6404096, SEEK_END);
|
|
+ if (fread (head, 1, 32, ifp) && !strcmp(head,"BRCMn")) {
|
|
strcpy (make, "OmniVision");
|
|
data_offset = ftell(ifp) + 0x8000-32;
|
|
width = raw_width;
|
|
@@ -8618,6 +8701,7 @@
|
|
filters = 0x16161616;
|
|
} else is_raw = 0;
|
|
}
|
|
+ }
|
|
|
|
for (i=0; i < sizeof corp / sizeof *corp; i++)
|
|
if (strcasestr (make, corp[i])) /* Simplify company names */
|
|
@@ -8649,7 +8733,7 @@
|
|
if (height == 3136 && width == 4864) /* Pentax K20D and Samsung GX20 */
|
|
{ height = 3124; width = 4688; filters = 0x16161616; }
|
|
if (width == 4352 && (!strcmp(model,"K-r") || !strcmp(model,"K-x")))
|
|
- { width = 4309; filters = 0x16161616; }
|
|
+/*RT*/ { width = 4308; filters = 0x16161616; }
|
|
if (width >= 4960 && !strncmp(model,"K-5",3))
|
|
{ left_margin = 10; width = 4950; filters = 0x16161616; }
|
|
if (width == 4736 && !strcmp(model,"K-7"))
|
|
@@ -8669,6 +8753,7 @@
|
|
case 0:
|
|
case 1: load_raw = &CLASS packed_dng_load_raw; break;
|
|
case 7: load_raw = &CLASS lossless_dng_load_raw; break;
|
|
+ case 8: load_raw = &CLASS deflate_dng_load_raw; break;
|
|
case 34892: load_raw = &CLASS lossy_dng_load_raw; break;
|
|
default: load_raw = 0;
|
|
}
|
|
@@ -8725,6 +8810,7 @@
|
|
if (height > width) pixel_aspect = 2;
|
|
filters = 0;
|
|
simple_coeff(0);
|
|
+ adobe_coeff (make, model);
|
|
} else if (!strcmp(make,"Canon") && tiff_bps == 15) {
|
|
switch (width) {
|
|
case 3344: width -= 66;
|
|
@@ -9034,24 +9120,53 @@
|
|
if (load_raw == &CLASS lossless_jpeg_load_raw)
|
|
load_raw = &CLASS hasselblad_load_raw;
|
|
if (raw_width == 7262) {
|
|
+ if (!strcmp(model, "H3D")) strcpy(model, "H3D-39"); // RT
|
|
height = 5444;
|
|
width = 7248;
|
|
top_margin = 4;
|
|
left_margin = 7;
|
|
filters = 0x61616161;
|
|
- } else if (raw_width == 7410 || raw_width == 8282) {
|
|
- height -= 84;
|
|
- width -= 82;
|
|
+ } else if (raw_width == 7410) {
|
|
+ if (!strcmp(model, "H4D")) strcpy(model, "H4D-40"); // RT
|
|
+ height = 5502;
|
|
+ width = 7328;
|
|
top_margin = 4;
|
|
left_margin = 41;
|
|
filters = 0x61616161;
|
|
+ } else if (raw_width == 6542) { // RT, H3D-31, H3DII-31, H4D-31
|
|
+ if (!strcmp(model, "H3D")) strcpy(model, "H3D-31");
|
|
+ if (!strcmp(model, "H4D")) strcpy(model, "H4D-31");
|
|
+ height = 4904;
|
|
+ width = 6524;
|
|
+ top_margin = 4;
|
|
+ left_margin = 8;
|
|
+ } else if (raw_width == 8282) { // RT, H3DII-50, H3DII-50MS, CFV-50, H4D-50
|
|
+ if (!strcmp(model, "H3D")) strcpy(model, "H3DII-50");
|
|
+ if (!strcmp(model, "H4D")) strcpy(model, "H4D-50");
|
|
+ height = 6152;
|
|
+ width = 8196;
|
|
+ top_margin = 4;
|
|
+ left_margin = 44;
|
|
+ } else if (raw_width == 8374) { // RT, CFV-50c, H5D-50c, "H5D-50c MS", "H5D-200c MS"
|
|
+ if (!strcmp(model, "H5D")) strcpy(model, "H5D-50c");
|
|
+ if (!strcmp(model, "CFV-2")) strcpy(model, "CFV-50c");
|
|
+ height = 6208;
|
|
+ width = 8280;
|
|
+ top_margin = 96;
|
|
+ left_margin = 48;
|
|
} else if (raw_width == 9044) {
|
|
height = 6716;
|
|
width = 8964;
|
|
top_margin = 8;
|
|
left_margin = 40;
|
|
- black += load_flags = 256;
|
|
- maximum = 0x8101;
|
|
+ // RT: removed black level / maximum adjustment, as it does not seem to be correct when there are clipped highlights. Tested with Hasselblad H4D-60.
|
|
+ //black += load_flags = 256;
|
|
+ //maximum = 0x8101;
|
|
+ } else if (raw_width == 4096) { // RT: CF-22 etc
|
|
+ if (!strcmp(model, "H3D")) strcpy(model, "H3D-22");
|
|
+ else if (strstr(model3, "Hasselblad ") == model3) strcpy(model, &model3[11]);
|
|
+ if (strstr(model3, "ixpressCF132")) strcpy(model, "CF-22"); // ixpressCF132 / CF132 is same as CF-22, we use the simpler name
|
|
+ else if (strstr(model3, "Hasselblad96")) strcpy(model, "CFV"); // popularly called CFV-16
|
|
} else if (raw_width == 4090) {
|
|
strcpy (model, "V96C");
|
|
height -= (top_margin = 6);
|
|
@@ -9109,6 +9224,7 @@
|
|
filters = 0x16161616;
|
|
}
|
|
} else if (!strcmp(make,"Leica") || !strcmp(make,"Panasonic")) {
|
|
+ if(raw_width > 0) { // avoid divide by zero
|
|
if ((flen - data_offset) / (raw_width*8/7) == raw_height)
|
|
load_raw = &CLASS panasonic_load_raw;
|
|
if (!load_raw) {
|
|
@@ -9126,6 +9242,7 @@
|
|
}
|
|
filters = 0x01010101 * (uchar) "\x94\x61\x49\x16"
|
|
[((filters-1) ^ (left_margin & 1) ^ (top_margin << 1)) & 3];
|
|
+ }
|
|
} else if (!strcmp(model,"C770UZ")) {
|
|
height = 1718;
|
|
width = 2304;
|
|
@@ -9201,13 +9318,15 @@
|
|
width -= 6;
|
|
} else if (!strcmp(make,"Sony") && raw_width == 7392) {
|
|
width -= 30;
|
|
- } else if (!strcmp(make,"Sony") && raw_width == 8000) {
|
|
- width -= 32;
|
|
- if (!strncmp(model,"DSC",3)) {
|
|
- tiff_bps = 14;
|
|
- load_raw = &CLASS unpacked_load_raw;
|
|
- black = 512;
|
|
- }
|
|
+// this was introduced with update to dcraw 9.27
|
|
+// but led to broken decode for compressed files from Sony DSC-RX1RM2
|
|
+// } else if (!strcmp(make,"Sony") && raw_width == 8000) {
|
|
+// width -= 32;
|
|
+// if (!strncmp(model,"DSC",3)) {
|
|
+// tiff_bps = 14;
|
|
+// load_raw = &CLASS unpacked_load_raw;
|
|
+// black = 512;
|
|
+// }
|
|
} else if (!strcmp(model,"DSLR-A100")) {
|
|
if (width == 3880) {
|
|
height--;
|
|
@@ -9357,6 +9476,20 @@
|
|
memcpy (rgb_cam, cmatrix, sizeof cmatrix);
|
|
raw_color = 0;
|
|
}
|
|
+ if(!strncmp(make, "Panasonic", 9) && !strncmp(model, "DMC-LX100",9))
|
|
+ adobe_coeff (make, model);
|
|
+ if(!strncmp(make, "Samsung", 7) && !strncmp(model, "GX20",4))
|
|
+ adobe_coeff (make, model);
|
|
+ if(!strncmp(make, "Samsung", 7) && !strncmp(model, "NX1",3))
|
|
+ adobe_coeff (make, model);
|
|
+ if(!strncmp(make, "Pentax", 6) && (!strncmp(model, "K10D",4) || !strncmp(model, "K-70",4)))
|
|
+ adobe_coeff (make, model);
|
|
+ if(!strncmp(make, "Leica", 5) && !strncmp(model, "Q",1))
|
|
+ adobe_coeff (make, model);
|
|
+ if(!strncmp(make, "Leica", 5) && !strncmp(model, "SL",2))
|
|
+ adobe_coeff (make, model);
|
|
+ if(!strncmp(make, "XIAOYI", 6) && !strncmp(model, "M1",2))
|
|
+ adobe_coeff (make, model);
|
|
if (raw_color) adobe_coeff (make, model);
|
|
if (load_raw == &CLASS kodak_radc_load_raw)
|
|
if (raw_color) adobe_coeff ("Apple","Quicktake");
|
|
@@ -9371,9 +9504,9 @@
|
|
if (raw_width < width ) raw_width = width;
|
|
}
|
|
if (!tiff_bps) tiff_bps = 12;
|
|
- if (!maximum) maximum = (1 << tiff_bps) - 1;
|
|
+ if (!maximum) maximum = ((uint64_t)1 << tiff_bps) - 1; // use uint64_t to avoid overflow if tiff_bps == 32
|
|
if (!load_raw || height < 22 || width < 22 ||
|
|
- tiff_bps > 16 || tiff_samples > 6 || colors > 4)
|
|
+ tiff_samples > 6 || colors > 4)
|
|
is_raw = 0;
|
|
#ifdef NO_JASPER
|
|
if (load_raw == &CLASS redcine_load_raw) {
|
|
@@ -9402,249 +9535,300 @@
|
|
if (flip == UINT_MAX) flip = 0;
|
|
}
|
|
|
|
-#ifndef NO_LCMS
|
|
-void CLASS apply_profile (const char *input, const char *output)
|
|
-{
|
|
- char *prof;
|
|
- cmsHPROFILE hInProfile=0, hOutProfile=0;
|
|
- cmsHTRANSFORM hTransform;
|
|
- FILE *fp;
|
|
- unsigned size;
|
|
-
|
|
- if (strcmp (input, "embed"))
|
|
- hInProfile = cmsOpenProfileFromFile (input, "r");
|
|
- else if (profile_length) {
|
|
- prof = (char *) malloc (profile_length);
|
|
- merror (prof, "apply_profile()");
|
|
- fseek (ifp, profile_offset, SEEK_SET);
|
|
- fread (prof, 1, profile_length, ifp);
|
|
- hInProfile = cmsOpenProfileFromMem (prof, profile_length);
|
|
- free (prof);
|
|
- } else
|
|
- fprintf (stderr,_("%s has no embedded profile.\n"), ifname);
|
|
- if (!hInProfile) return;
|
|
- if (!output)
|
|
- hOutProfile = cmsCreate_sRGBProfile();
|
|
- else if ((fp = fopen (output, "rb"))) {
|
|
- fread (&size, 4, 1, fp);
|
|
- fseek (fp, 0, SEEK_SET);
|
|
- oprof = (unsigned *) malloc (size = ntohl(size));
|
|
- merror (oprof, "apply_profile()");
|
|
- fread (oprof, 1, size, fp);
|
|
- fclose (fp);
|
|
- if (!(hOutProfile = cmsOpenProfileFromMem (oprof, size))) {
|
|
- free (oprof);
|
|
- oprof = 0;
|
|
+//#ifndef NO_LCMS
|
|
+//void CLASS apply_profile (const char *input, const char *output)
|
|
+//{
|
|
+// char *prof;
|
|
+// cmsHPROFILE hInProfile=0, hOutProfile=0;
|
|
+// cmsHTRANSFORM hTransform;
|
|
+// FILE *fp;
|
|
+// unsigned size;
|
|
+//
|
|
+// if (strcmp (input, "embed"))
|
|
+// hInProfile = cmsOpenProfileFromFile (input, "r");
|
|
+// else if (profile_length) {
|
|
+// prof = (char *) malloc (profile_length);
|
|
+// merror (prof, "apply_profile()");
|
|
+// fseek (ifp, profile_offset, SEEK_SET);
|
|
+// fread (prof, 1, profile_length, ifp);
|
|
+// hInProfile = cmsOpenProfileFromMem (prof, profile_length);
|
|
+// free (prof);
|
|
+// } else
|
|
+// fprintf (stderr,_("%s has no embedded profile.\n"), ifname);
|
|
+// if (!hInProfile) return;
|
|
+// if (!output)
|
|
+// hOutProfile = cmsCreate_sRGBProfile();
|
|
+// else if ((fp = fopen (output, "rb"))) {
|
|
+// fread (&size, 4, 1, fp);
|
|
+// fseek (fp, 0, SEEK_SET);
|
|
+// oprof = (unsigned *) malloc (size = ntohl(size));
|
|
+// merror (oprof, "apply_profile()");
|
|
+// fread (oprof, 1, size, fp);
|
|
+// fclose (fp);
|
|
+// if (!(hOutProfile = cmsOpenProfileFromMem (oprof, size))) {
|
|
+// free (oprof);
|
|
+// oprof = 0;
|
|
+// }
|
|
+// } else
|
|
+// fprintf (stderr,_("Cannot open file %s!\n"), output);
|
|
+// if (!hOutProfile) goto quit;
|
|
+// if (verbose)
|
|
+// fprintf (stderr,_("Applying color profile...\n"));
|
|
+// hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16,
|
|
+// hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
|
|
+// cmsDoTransform (hTransform, image, image, width*height);
|
|
+// raw_color = 1; /* Don't use rgb_cam with a profile */
|
|
+// cmsDeleteTransform (hTransform);
|
|
+// cmsCloseProfile (hOutProfile);
|
|
+//quit:
|
|
+// cmsCloseProfile (hInProfile);
|
|
+//}
|
|
+//#endif
|
|
+
|
|
+/* RT: DNG Float */
|
|
+
|
|
+#include <zlib.h>
|
|
+#include <stdint.h>
|
|
+
|
|
+static void decodeFPDeltaRow(Bytef * src, Bytef * dst, size_t tileWidth, size_t realTileWidth, int bytesps, int factor) {
|
|
+ // DecodeDeltaBytes
|
|
+ for (size_t col = factor; col < realTileWidth*bytesps; ++col) {
|
|
+ src[col] += src[col - factor];
|
|
+ }
|
|
+ // Reorder bytes into the image
|
|
+ // 16 and 32-bit versions depend on local architecture, 24-bit does not
|
|
+ if (bytesps == 3) {
|
|
+ for (size_t col = 0; col < tileWidth; ++col) {
|
|
+ dst[col*3] = src[col];
|
|
+ dst[col*3 + 1] = src[col + realTileWidth];
|
|
+ dst[col*3 + 2] = src[col + realTileWidth*2];
|
|
}
|
|
- } else
|
|
- fprintf (stderr,_("Cannot open file %s!\n"), output);
|
|
- if (!hOutProfile) goto quit;
|
|
- if (verbose)
|
|
- fprintf (stderr,_("Applying color profile...\n"));
|
|
- hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16,
|
|
- hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
|
|
- cmsDoTransform (hTransform, image, image, width*height);
|
|
- raw_color = 1; /* Don't use rgb_cam with a profile */
|
|
- cmsDeleteTransform (hTransform);
|
|
- cmsCloseProfile (hOutProfile);
|
|
-quit:
|
|
- cmsCloseProfile (hInProfile);
|
|
+ } else {
|
|
+ union X { uint32_t x; uint8_t c; };
|
|
+ if (((union X){1}).c) {
|
|
+ for (size_t col = 0; col < tileWidth; ++col) {
|
|
+ for (size_t byte = 0; byte < bytesps; ++byte)
|
|
+ dst[col*bytesps + byte] = src[col + realTileWidth*(bytesps-byte-1)]; // Little endian
|
|
+ }
|
|
+ } else {
|
|
+ for (size_t col = 0; col < tileWidth; ++col) {
|
|
+ for (size_t byte = 0; byte < bytesps; ++byte)
|
|
+ dst[col*bytesps + byte] = src[col + realTileWidth*byte];
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
}
|
|
-#endif
|
|
|
|
-void CLASS convert_to_rgb()
|
|
-{
|
|
- int row, col, c, i, j, k;
|
|
- ushort *img;
|
|
- float out[3], out_cam[3][4];
|
|
- double num, inverse[3][3];
|
|
- static const double xyzd50_srgb[3][3] =
|
|
- { { 0.436083, 0.385083, 0.143055 },
|
|
- { 0.222507, 0.716888, 0.060608 },
|
|
- { 0.013930, 0.097097, 0.714022 } };
|
|
- static const double rgb_rgb[3][3] =
|
|
- { { 1,0,0 }, { 0,1,0 }, { 0,0,1 } };
|
|
- static const double adobe_rgb[3][3] =
|
|
- { { 0.715146, 0.284856, 0.000000 },
|
|
- { 0.000000, 1.000000, 0.000000 },
|
|
- { 0.000000, 0.041166, 0.958839 } };
|
|
- static const double wide_rgb[3][3] =
|
|
- { { 0.593087, 0.404710, 0.002206 },
|
|
- { 0.095413, 0.843149, 0.061439 },
|
|
- { 0.011621, 0.069091, 0.919288 } };
|
|
- static const double prophoto_rgb[3][3] =
|
|
- { { 0.529317, 0.330092, 0.140588 },
|
|
- { 0.098368, 0.873465, 0.028169 },
|
|
- { 0.016879, 0.117663, 0.865457 } };
|
|
- static const double aces_rgb[3][3] =
|
|
- { { 0.432996, 0.375380, 0.189317 },
|
|
- { 0.089427, 0.816523, 0.102989 },
|
|
- { 0.019165, 0.118150, 0.941914 } };
|
|
- static const double (*out_rgb[])[3] =
|
|
- { rgb_rgb, adobe_rgb, wide_rgb, prophoto_rgb, xyz_rgb, aces_rgb };
|
|
- static const char *name[] =
|
|
- { "sRGB", "Adobe RGB (1998)", "WideGamut D65", "ProPhoto D65", "XYZ", "ACES" };
|
|
- static const unsigned phead[] =
|
|
- { 1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0, 0, 0,
|
|
- 0x61637370, 0, 0, 0x6e6f6e65, 0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d };
|
|
- unsigned pbody[] =
|
|
- { 10, 0x63707274, 0, 36, /* cprt */
|
|
- 0x64657363, 0, 40, /* desc */
|
|
- 0x77747074, 0, 20, /* wtpt */
|
|
- 0x626b7074, 0, 20, /* bkpt */
|
|
- 0x72545243, 0, 14, /* rTRC */
|
|
- 0x67545243, 0, 14, /* gTRC */
|
|
- 0x62545243, 0, 14, /* bTRC */
|
|
- 0x7258595a, 0, 20, /* rXYZ */
|
|
- 0x6758595a, 0, 20, /* gXYZ */
|
|
- 0x6258595a, 0, 20 }; /* bXYZ */
|
|
- static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc };
|
|
- unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 };
|
|
-
|
|
- gamma_curve (gamm[0], gamm[1], 0, 0);
|
|
- memcpy (out_cam, rgb_cam, sizeof out_cam);
|
|
- raw_color |= colors == 1 || document_mode ||
|
|
- output_color < 1 || output_color > 6;
|
|
- if (!raw_color) {
|
|
- oprof = (unsigned *) calloc (phead[0], 1);
|
|
- merror (oprof, "convert_to_rgb()");
|
|
- memcpy (oprof, phead, sizeof phead);
|
|
- if (output_color == 5) oprof[4] = oprof[5];
|
|
- oprof[0] = 132 + 12*pbody[0];
|
|
- for (i=0; i < pbody[0]; i++) {
|
|
- oprof[oprof[0]/4] = i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874;
|
|
- pbody[i*3+2] = oprof[0];
|
|
- oprof[0] += (pbody[i*3+3] + 3) & -4;
|
|
- }
|
|
- memcpy (oprof+32, pbody, sizeof pbody);
|
|
- oprof[pbody[5]/4+2] = strlen(name[output_color-1]) + 1;
|
|
- memcpy ((char *)oprof+pbody[8]+8, pwhite, sizeof pwhite);
|
|
- pcurve[3] = (short)(256/gamm[5]+0.5) << 16;
|
|
- for (i=4; i < 7; i++)
|
|
- memcpy ((char *)oprof+pbody[i*3+2], pcurve, sizeof pcurve);
|
|
- pseudoinverse ((double (*)[3]) out_rgb[output_color-1], inverse, 3);
|
|
- for (i=0; i < 3; i++)
|
|
- for (j=0; j < 3; j++) {
|
|
- for (num = k=0; k < 3; k++)
|
|
- num += xyzd50_srgb[i][k] * inverse[j][k];
|
|
- oprof[pbody[j*3+23]/4+i+2] = num * 0x10000 + 0.5;
|
|
- }
|
|
- for (i=0; i < phead[0]/4; i++)
|
|
- oprof[i] = htonl(oprof[i]);
|
|
- strcpy ((char *)oprof+pbody[2]+8, "auto-generated by dcraw");
|
|
- strcpy ((char *)oprof+pbody[5]+12, name[output_color-1]);
|
|
- for (i=0; i < 3; i++)
|
|
- for (j=0; j < colors; j++)
|
|
- for (out_cam[i][j] = k=0; k < 3; k++)
|
|
- out_cam[i][j] += out_rgb[output_color-1][i][k] * rgb_cam[k][j];
|
|
+// From DNG SDK dng_utils.h
|
|
+static inline uint32_t DNG_HalfToFloat(uint16_t halfValue) {
|
|
+ int32_t sign = (halfValue >> 15) & 0x00000001;
|
|
+ int32_t exponent = (halfValue >> 10) & 0x0000001f;
|
|
+ int32_t mantissa = halfValue & 0x000003ff;
|
|
+ if (exponent == 0) {
|
|
+ if (mantissa == 0) {
|
|
+ // Plus or minus zero
|
|
+ return (uint32_t) (sign << 31);
|
|
+ } else {
|
|
+ // Denormalized number -- renormalize it
|
|
+ while (!(mantissa & 0x00000400)) {
|
|
+ mantissa <<= 1;
|
|
+ exponent -= 1;
|
|
+ }
|
|
+ exponent += 1;
|
|
+ mantissa &= ~0x00000400;
|
|
+ }
|
|
+ } else if (exponent == 31) {
|
|
+ if (mantissa == 0) {
|
|
+ // Positive or negative infinity, convert to maximum (16 bit) values.
|
|
+ return (uint32_t) ((sign << 31) | ((0x1eL + 127 - 15) << 23) | (0x3ffL << 13));
|
|
+ } else {
|
|
+ // Nan -- Just set to zero.
|
|
+ return 0;
|
|
+ }
|
|
}
|
|
- if (verbose)
|
|
- fprintf (stderr, raw_color ? _("Building histograms...\n") :
|
|
- _("Converting to %s colorspace...\n"), name[output_color-1]);
|
|
+ // Normalized number
|
|
+ exponent += (127 - 15);
|
|
+ mantissa <<= 13;
|
|
+ // Assemble sign, exponent and mantissa.
|
|
+ return (uint32_t) ((sign << 31) | (exponent << 23) | mantissa);
|
|
+}
|
|
|
|
- memset (histogram, 0, sizeof histogram);
|
|
- for (img=image[0], row=0; row < height; row++)
|
|
- for (col=0; col < width; col++, img+=4) {
|
|
- if (!raw_color) {
|
|
- out[0] = out[1] = out[2] = 0;
|
|
- FORCC {
|
|
- out[0] += out_cam[0][c] * img[c];
|
|
- out[1] += out_cam[1][c] * img[c];
|
|
- out[2] += out_cam[2][c] * img[c];
|
|
- }
|
|
- FORC3 img[c] = CLIP((int) out[c]);
|
|
- }
|
|
- else if (document_mode)
|
|
- img[0] = img[fcol(row,col)];
|
|
- FORCC histogram[c][img[c] >> 3]++;
|
|
- }
|
|
- if (colors == 4 && output_color) colors = 3;
|
|
- if (document_mode && filters) colors = 1;
|
|
-}
|
|
-
|
|
-void CLASS fuji_rotate()
|
|
-{
|
|
- int i, row, col;
|
|
- double step;
|
|
- float r, c, fr, fc;
|
|
- unsigned ur, uc;
|
|
- ushort wide, high, (*img)[4], (*pix)[4];
|
|
+static inline uint32_t DNG_FP24ToFloat(const uint8_t * input) {
|
|
+ int32_t sign = (input [0] >> 7) & 0x01;
|
|
+ int32_t exponent = (input [0] ) & 0x7F;
|
|
+ int32_t mantissa = (((int32_t) input [1]) << 8) | input[2];
|
|
+ if (exponent == 0) {
|
|
+ if (mantissa == 0) {
|
|
+ // Plus or minus zero
|
|
+ return (uint32_t) (sign << 31);
|
|
+ } else {
|
|
+ // Denormalized number -- renormalize it
|
|
+ while (!(mantissa & 0x00010000)) {
|
|
+ mantissa <<= 1;
|
|
+ exponent -= 1;
|
|
+ }
|
|
+ exponent += 1;
|
|
+ mantissa &= ~0x00010000;
|
|
+ }
|
|
+ } else if (exponent == 127) {
|
|
+ if (mantissa == 0) {
|
|
+ // Positive or negative infinity, convert to maximum (24 bit) values.
|
|
+ return (uint32_t) ((sign << 31) | ((0x7eL + 128 - 64) << 23) | (0xffffL << 7));
|
|
+ } else {
|
|
+ // Nan -- Just set to zero.
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ // Normalized number
|
|
+ exponent += (128 - 64);
|
|
+ mantissa <<= 7;
|
|
+ // Assemble sign, exponent and mantissa.
|
|
+ return (uint32_t) ((sign << 31) | (exponent << 23) | mantissa);
|
|
+}
|
|
|
|
- if (!fuji_width) return;
|
|
- if (verbose)
|
|
- fprintf (stderr,_("Rotating image 45 degrees...\n"));
|
|
- fuji_width = (fuji_width - 1 + shrink) >> shrink;
|
|
- step = sqrt(0.5);
|
|
- wide = fuji_width / step;
|
|
- high = (height - fuji_width) / step;
|
|
- img = (ushort (*)[4]) calloc (high, wide*sizeof *img);
|
|
- merror (img, "fuji_rotate()");
|
|
-
|
|
- for (row=0; row < high; row++)
|
|
- for (col=0; col < wide; col++) {
|
|
- ur = r = fuji_width + (row-col)*step;
|
|
- uc = c = (row+col)*step;
|
|
- if (ur > height-2 || uc > width-2) continue;
|
|
- fr = r - ur;
|
|
- fc = c - uc;
|
|
- pix = image + ur*width + uc;
|
|
- for (i=0; i < colors; i++)
|
|
- img[row*wide+col][i] =
|
|
- (pix[ 0][i]*(1-fc) + pix[ 1][i]*fc) * (1-fr) +
|
|
- (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr;
|
|
- }
|
|
- free (image);
|
|
- width = wide;
|
|
- height = high;
|
|
- image = img;
|
|
- fuji_width = 0;
|
|
+static void expandFloats(Bytef * dst, int tileWidth, int bytesps) {
|
|
+ if (bytesps == 2) {
|
|
+ uint16_t * dst16 = (uint16_t *) dst;
|
|
+ uint32_t * dst32 = (uint32_t *) dst;
|
|
+ for (int index = tileWidth - 1; index >= 0; --index) {
|
|
+ dst32[index] = DNG_HalfToFloat(dst16[index]);
|
|
+ }
|
|
+ } else if (bytesps == 3) {
|
|
+ uint8_t * dst8 = ((uint8_t *) dst) + (tileWidth - 1) * 3;
|
|
+ uint32_t * dst32 = (uint32_t *) dst;
|
|
+ for (int index = tileWidth - 1; index >= 0; --index, dst8 -= 3) {
|
|
+ dst32[index] = DNG_FP24ToFloat(dst8);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
-void CLASS stretch()
|
|
+static void copyFloatDataToInt(float * src, ushort * dst, size_t size, float max) {
|
|
+ bool negative = false, nan = false;
|
|
+
|
|
+#ifdef _OPENMP
|
|
+#pragma omp parallel for
|
|
+#endif
|
|
+ for (size_t i = 0; i < size; ++i) {
|
|
+ if (src[i] < 0.0f) {
|
|
+ negative = true;
|
|
+ src[i] = 0.0f;
|
|
+ } else if (std::isnan(src[i])) {
|
|
+ nan = true;
|
|
+ src[i] = max;
|
|
+ }
|
|
+ // Copy the data to the integer buffer to build the thumbnail
|
|
+ dst[i] = (ushort)src[i];
|
|
+ }
|
|
+ if (negative)
|
|
+ fprintf(stderr, "DNG Float: Negative data found in input file\n");
|
|
+ if (nan)
|
|
+ fprintf(stderr, "DNG Float: NaN data found in input file\n");
|
|
+}
|
|
+
|
|
+void CLASS deflate_dng_load_raw() {
|
|
+ struct tiff_ifd * ifd = &tiff_ifd[0];
|
|
+ while (ifd < &tiff_ifd[tiff_nifds] && ifd->offset != data_offset) ++ifd;
|
|
+ if (ifd == &tiff_ifd[tiff_nifds]) {
|
|
+ fprintf(stderr, "DNG Deflate: Raw image not found???\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ int predFactor;
|
|
+ switch(ifd->predictor) {
|
|
+ case 3: predFactor = 1; break;
|
|
+ case 34894: predFactor = 2; break;
|
|
+ case 34895: predFactor = 4; break;
|
|
+ default: predFactor = 0; break;
|
|
+ }
|
|
+
|
|
+ if (ifd->sample_format == 3) { // Floating point data
|
|
+ float_raw_image = new float[raw_width * raw_height];
|
|
+
|
|
+#ifdef _OPENMP
|
|
+#pragma omp parallel for
|
|
+#endif
|
|
+ for (size_t i = 0; i < raw_width * raw_height; ++i)
|
|
+ float_raw_image[i] = 0.0f;
|
|
+ }
|
|
+
|
|
+ // NOTE: This reader is based on the official DNG SDK from Adobe.
|
|
+ // It assumes tiles without subtiles, but the standard does not say that
|
|
+ // subtiles or strips couldn't be used.
|
|
+ if (tile_length < INT_MAX) {
|
|
+ size_t tilesWide = (raw_width + tile_width - 1) / tile_width;
|
|
+ size_t tilesHigh = (raw_height + tile_length - 1) / tile_length;
|
|
+ size_t tileCount = tilesWide * tilesHigh;
|
|
+ //fprintf(stderr, "%dx%d tiles, %d total\n", tilesWide, tilesHigh, tileCount);
|
|
+ size_t tileOffsets[tileCount];
|
|
+ for (size_t t = 0; t < tileCount; ++t) {
|
|
+ tileOffsets[t] = get4();
|
|
+ }
|
|
+ size_t tileBytes[tileCount];
|
|
+ uLongf maxCompressed = 0;
|
|
+ if (tileCount == 1) {
|
|
+ tileBytes[0] = maxCompressed = ifd->bytes;
|
|
+ } else {
|
|
+ fseek(ifp, ifd->bytes, SEEK_SET);
|
|
+ for (size_t t = 0; t < tileCount; ++t) {
|
|
+ tileBytes[t] = get4();
|
|
+ //fprintf(stderr, "Tile %d at %d, size %d\n", t, tileOffsets[t], tileBytes[t]);
|
|
+ if (maxCompressed < tileBytes[t])
|
|
+ maxCompressed = tileBytes[t];
|
|
+ }
|
|
+ }
|
|
+ uLongf dstLen = tile_width * tile_length * 4;
|
|
+
|
|
+#ifdef _OPENMP
|
|
+#pragma omp parallel
|
|
+#endif
|
|
{
|
|
- ushort newdim, (*img)[4], *pix0, *pix1;
|
|
- int row, col, c;
|
|
- double rc, frac;
|
|
+ Bytef * cBuffer = new Bytef[maxCompressed];
|
|
+ Bytef * uBuffer = new Bytef[dstLen];
|
|
|
|
- if (pixel_aspect == 1) return;
|
|
- if (verbose) fprintf (stderr,_("Stretching the image...\n"));
|
|
- if (pixel_aspect < 1) {
|
|
- newdim = height / pixel_aspect + 0.5;
|
|
- img = (ushort (*)[4]) calloc (width, newdim*sizeof *img);
|
|
- merror (img, "stretch()");
|
|
- for (rc=row=0; row < newdim; row++, rc+=pixel_aspect) {
|
|
- frac = rc - (c = rc);
|
|
- pix0 = pix1 = image[c*width];
|
|
- if (c+1 < height) pix1 += width*4;
|
|
- for (col=0; col < width; col++, pix0+=4, pix1+=4)
|
|
- FORCC img[row*width+col][c] = pix0[c]*(1-frac) + pix1[c]*frac + 0.5;
|
|
+#ifdef _OPENMP
|
|
+#pragma omp for collapse(2) nowait
|
|
+#endif
|
|
+ for (size_t y = 0; y < raw_height; y += tile_length) {
|
|
+ for (size_t x = 0; x < raw_width; x += tile_width) {
|
|
+ size_t t = (y / tile_length) * tilesWide + (x / tile_width);
|
|
+#pragma omp critical
|
|
+{
|
|
+ fseek(ifp, tileOffsets[t], SEEK_SET);
|
|
+ fread(cBuffer, 1, tileBytes[t], ifp);
|
|
+}
|
|
+ int err = uncompress(uBuffer, &dstLen, cBuffer, tileBytes[t]);
|
|
+ if (err != Z_OK) {
|
|
+ fprintf(stderr, "DNG Deflate: Failed uncompressing tile %d, with error %d\n", (int)t, err);
|
|
+ } else if (ifd->sample_format == 3) { // Floating point data
|
|
+ int bytesps = ifd->bps >> 3;
|
|
+ size_t thisTileLength = y + tile_length > raw_height ? raw_height - y : tile_length;
|
|
+ size_t thisTileWidth = x + tile_width > raw_width ? raw_width - x : tile_width;
|
|
+ for (size_t row = 0; row < thisTileLength; ++row) {
|
|
+ Bytef * src = uBuffer + row*tile_width*bytesps;
|
|
+ Bytef * dst = (Bytef *)&float_raw_image[(y+row)*raw_width + x];
|
|
+ if (predFactor)
|
|
+ decodeFPDeltaRow(src, dst, thisTileWidth, tile_width, bytesps, predFactor);
|
|
+ expandFloats(dst, thisTileWidth, bytesps);
|
|
+ }
|
|
+ } else { // 32-bit Integer data
|
|
+ // TODO
|
|
+ }
|
|
+ }
|
|
}
|
|
- height = newdim;
|
|
- } else {
|
|
- newdim = width * pixel_aspect + 0.5;
|
|
- img = (ushort (*)[4]) calloc (height, newdim*sizeof *img);
|
|
- merror (img, "stretch()");
|
|
- for (rc=col=0; col < newdim; col++, rc+=1/pixel_aspect) {
|
|
- frac = rc - (c = rc);
|
|
- pix0 = pix1 = image[c];
|
|
- if (c+1 < width) pix1 += 4;
|
|
- for (row=0; row < height; row++, pix0+=width*4, pix1+=width*4)
|
|
- FORCC img[row*newdim+col][c] = pix0[c]*(1-frac) + pix1[c]*frac + 0.5;
|
|
- }
|
|
- width = newdim;
|
|
- }
|
|
- free (image);
|
|
- image = img;
|
|
-}
|
|
-
|
|
-int CLASS flip_index (int row, int col)
|
|
-{
|
|
- if (flip & 4) SWAP(row,col);
|
|
- if (flip & 2) row = iheight - 1 - row;
|
|
- if (flip & 1) col = iwidth - 1 - col;
|
|
- return row * iwidth + col;
|
|
+
|
|
+ delete [] cBuffer;
|
|
+ delete [] uBuffer;
|
|
+}
|
|
+ }
|
|
+
|
|
+ if (ifd->sample_format == 3) { // Floating point data
|
|
+ copyFloatDataToInt(float_raw_image, raw_image, raw_width*raw_height, maximum);
|
|
+ }
|
|
}
|
|
|
|
+/* RT: removed unused functions */
|
|
+
|
|
struct tiff_tag {
|
|
ushort tag, type;
|
|
int count;
|
|
@@ -9667,594 +9851,11 @@
|
|
char desc[512], make[64], model[64], soft[32], date[20], artist[64];
|
|
};
|
|
|
|
-void CLASS tiff_set (struct tiff_hdr *th, ushort *ntag,
|
|
- ushort tag, ushort type, int count, int val)
|
|
-{
|
|
- struct tiff_tag *tt;
|
|
- int c;
|
|
-
|
|
- tt = (struct tiff_tag *)(ntag+1) + (*ntag)++;
|
|
- tt->val.i = val;
|
|
- if (type == 1 && count <= 4)
|
|
- FORC(4) tt->val.c[c] = val >> (c << 3);
|
|
- else if (type == 2) {
|
|
- count = strnlen((char *)th + val, count-1) + 1;
|
|
- if (count <= 4)
|
|
- FORC(4) tt->val.c[c] = ((char *)th)[val+c];
|
|
- } else if (type == 3 && count <= 2)
|
|
- FORC(2) tt->val.s[c] = val >> (c << 4);
|
|
- tt->count = count;
|
|
- tt->type = type;
|
|
- tt->tag = tag;
|
|
-}
|
|
-
|
|
-#define TOFF(ptr) ((char *)(&(ptr)) - (char *)th)
|
|
-
|
|
-void CLASS tiff_head (struct tiff_hdr *th, int full)
|
|
-{
|
|
- int c, psize=0;
|
|
- struct tm *t;
|
|
-
|
|
- memset (th, 0, sizeof *th);
|
|
- th->order = htonl(0x4d4d4949) >> 16;
|
|
- th->magic = 42;
|
|
- th->ifd = 10;
|
|
- th->rat[0] = th->rat[2] = 300;
|
|
- th->rat[1] = th->rat[3] = 1;
|
|
- FORC(6) th->rat[4+c] = 1000000;
|
|
- th->rat[4] *= shutter;
|
|
- th->rat[6] *= aperture;
|
|
- th->rat[8] *= focal_len;
|
|
- strncpy (th->desc, desc, 512);
|
|
- strncpy (th->make, make, 64);
|
|
- strncpy (th->model, model, 64);
|
|
- strcpy (th->soft, "dcraw v"DCRAW_VERSION);
|
|
- t = localtime (×tamp);
|
|
- sprintf (th->date, "%04d:%02d:%02d %02d:%02d:%02d",
|
|
- t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
|
|
- strncpy (th->artist, artist, 64);
|
|
- if (full) {
|
|
- tiff_set (th, &th->ntag, 254, 4, 1, 0);
|
|
- tiff_set (th, &th->ntag, 256, 4, 1, width);
|
|
- tiff_set (th, &th->ntag, 257, 4, 1, height);
|
|
- tiff_set (th, &th->ntag, 258, 3, colors, output_bps);
|
|
- if (colors > 2)
|
|
- th->tag[th->ntag-1].val.i = TOFF(th->bps);
|
|
- FORC4 th->bps[c] = output_bps;
|
|
- tiff_set (th, &th->ntag, 259, 3, 1, 1);
|
|
- tiff_set (th, &th->ntag, 262, 3, 1, 1 + (colors > 1));
|
|
- }
|
|
- tiff_set (th, &th->ntag, 270, 2, 512, TOFF(th->desc));
|
|
- tiff_set (th, &th->ntag, 271, 2, 64, TOFF(th->make));
|
|
- tiff_set (th, &th->ntag, 272, 2, 64, TOFF(th->model));
|
|
- if (full) {
|
|
- if (oprof) psize = ntohl(oprof[0]);
|
|
- tiff_set (th, &th->ntag, 273, 4, 1, sizeof *th + psize);
|
|
- tiff_set (th, &th->ntag, 277, 3, 1, colors);
|
|
- tiff_set (th, &th->ntag, 278, 4, 1, height);
|
|
- tiff_set (th, &th->ntag, 279, 4, 1, height*width*colors*output_bps/8);
|
|
- } else
|
|
- tiff_set (th, &th->ntag, 274, 3, 1, "12435867"[flip]-'0');
|
|
- tiff_set (th, &th->ntag, 282, 5, 1, TOFF(th->rat[0]));
|
|
- tiff_set (th, &th->ntag, 283, 5, 1, TOFF(th->rat[2]));
|
|
- tiff_set (th, &th->ntag, 284, 3, 1, 1);
|
|
- tiff_set (th, &th->ntag, 296, 3, 1, 2);
|
|
- tiff_set (th, &th->ntag, 305, 2, 32, TOFF(th->soft));
|
|
- tiff_set (th, &th->ntag, 306, 2, 20, TOFF(th->date));
|
|
- tiff_set (th, &th->ntag, 315, 2, 64, TOFF(th->artist));
|
|
- tiff_set (th, &th->ntag, 34665, 4, 1, TOFF(th->nexif));
|
|
- if (psize) tiff_set (th, &th->ntag, 34675, 7, psize, sizeof *th);
|
|
- tiff_set (th, &th->nexif, 33434, 5, 1, TOFF(th->rat[4]));
|
|
- tiff_set (th, &th->nexif, 33437, 5, 1, TOFF(th->rat[6]));
|
|
- tiff_set (th, &th->nexif, 34855, 3, 1, iso_speed);
|
|
- tiff_set (th, &th->nexif, 37386, 5, 1, TOFF(th->rat[8]));
|
|
- if (gpsdata[1]) {
|
|
- tiff_set (th, &th->ntag, 34853, 4, 1, TOFF(th->ngps));
|
|
- tiff_set (th, &th->ngps, 0, 1, 4, 0x202);
|
|
- tiff_set (th, &th->ngps, 1, 2, 2, gpsdata[29]);
|
|
- tiff_set (th, &th->ngps, 2, 5, 3, TOFF(th->gps[0]));
|
|
- tiff_set (th, &th->ngps, 3, 2, 2, gpsdata[30]);
|
|
- tiff_set (th, &th->ngps, 4, 5, 3, TOFF(th->gps[6]));
|
|
- tiff_set (th, &th->ngps, 5, 1, 1, gpsdata[31]);
|
|
- tiff_set (th, &th->ngps, 6, 5, 1, TOFF(th->gps[18]));
|
|
- tiff_set (th, &th->ngps, 7, 5, 3, TOFF(th->gps[12]));
|
|
- tiff_set (th, &th->ngps, 18, 2, 12, TOFF(th->gps[20]));
|
|
- tiff_set (th, &th->ngps, 29, 2, 12, TOFF(th->gps[23]));
|
|
- memcpy (th->gps, gpsdata, sizeof th->gps);
|
|
- }
|
|
-}
|
|
-
|
|
-void CLASS jpeg_thumb()
|
|
-{
|
|
- char *thumb;
|
|
- ushort exif[5];
|
|
- struct tiff_hdr th;
|
|
-
|
|
- thumb = (char *) malloc (thumb_length);
|
|
- merror (thumb, "jpeg_thumb()");
|
|
- fread (thumb, 1, thumb_length, ifp);
|
|
- fputc (0xff, ofp);
|
|
- fputc (0xd8, ofp);
|
|
- if (strcmp (thumb+6, "Exif")) {
|
|
- memcpy (exif, "\xff\xe1 Exif\0\0", 10);
|
|
- exif[1] = htons (8 + sizeof th);
|
|
- fwrite (exif, 1, sizeof exif, ofp);
|
|
- tiff_head (&th, 0);
|
|
- fwrite (&th, 1, sizeof th, ofp);
|
|
- }
|
|
- fwrite (thumb+2, 1, thumb_length-2, ofp);
|
|
- free (thumb);
|
|
-}
|
|
-
|
|
-void CLASS write_ppm_tiff()
|
|
-{
|
|
- struct tiff_hdr th;
|
|
- uchar *ppm;
|
|
- ushort *ppm2;
|
|
- int c, row, col, soff, rstep, cstep;
|
|
- int perc, val, total, white=0x2000;
|
|
-
|
|
- perc = width * height * 0.01; /* 99th percentile white level */
|
|
- if (fuji_width) perc /= 2;
|
|
- if (!((highlight & ~2) || no_auto_bright))
|
|
- for (white=c=0; c < colors; c++) {
|
|
- for (val=0x2000, total=0; --val > 32; )
|
|
- if ((total += histogram[c][val]) > perc) break;
|
|
- if (white < val) white = val;
|
|
- }
|
|
- gamma_curve (gamm[0], gamm[1], 2, (white << 3)/bright);
|
|
- iheight = height;
|
|
- iwidth = width;
|
|
- if (flip & 4) SWAP(height,width);
|
|
- ppm = (uchar *) calloc (width, colors*output_bps/8);
|
|
- ppm2 = (ushort *) ppm;
|
|
- merror (ppm, "write_ppm_tiff()");
|
|
- if (output_tiff) {
|
|
- tiff_head (&th, 1);
|
|
- fwrite (&th, sizeof th, 1, ofp);
|
|
- if (oprof)
|
|
- fwrite (oprof, ntohl(oprof[0]), 1, ofp);
|
|
- } else if (colors > 3)
|
|
- fprintf (ofp,
|
|
- "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nTUPLTYPE %s\nENDHDR\n",
|
|
- width, height, colors, (1 << output_bps)-1, cdesc);
|
|
- else
|
|
- fprintf (ofp, "P%d\n%d %d\n%d\n",
|
|
- colors/2+5, width, height, (1 << output_bps)-1);
|
|
- soff = flip_index (0, 0);
|
|
- cstep = flip_index (0, 1) - soff;
|
|
- rstep = flip_index (1, 0) - flip_index (0, width);
|
|
- for (row=0; row < height; row++, soff += rstep) {
|
|
- for (col=0; col < width; col++, soff += cstep)
|
|
- if (output_bps == 8)
|
|
- FORCC ppm [col*colors+c] = curve[image[soff][c]] >> 8;
|
|
- else FORCC ppm2[col*colors+c] = curve[image[soff][c]];
|
|
- if (output_bps == 16 && !output_tiff && htons(0x55aa) != 0x55aa)
|
|
- swab (ppm2, ppm2, width*colors*2);
|
|
- fwrite (ppm, colors*output_bps/8, width, ofp);
|
|
- }
|
|
- free (ppm);
|
|
-}
|
|
-
|
|
-int CLASS main (int argc, const char **argv)
|
|
-{
|
|
- int arg, status=0, quality, i, c;
|
|
- int timestamp_only=0, thumbnail_only=0, identify_only=0;
|
|
- int user_qual=-1, user_black=-1, user_sat=-1, user_flip=-1;
|
|
- int use_fuji_rotate=1, write_to_stdout=0, read_from_stdin=0;
|
|
- const char *sp, *bpfile=0, *dark_frame=0, *write_ext;
|
|
- char opm, opt, *ofname, *cp;
|
|
- struct utimbuf ut;
|
|
-#ifndef NO_LCMS
|
|
- const char *cam_profile=0, *out_profile=0;
|
|
-#endif
|
|
-
|
|
-#ifndef LOCALTIME
|
|
- putenv ((char *) "TZ=UTC");
|
|
-#endif
|
|
-#ifdef LOCALEDIR
|
|
- setlocale (LC_CTYPE, "");
|
|
- setlocale (LC_MESSAGES, "");
|
|
- bindtextdomain ("dcraw", LOCALEDIR);
|
|
- textdomain ("dcraw");
|
|
-#endif
|
|
-
|
|
- if (argc == 1) {
|
|
- printf(_("\nRaw photo decoder \"dcraw\" v%s"), DCRAW_VERSION);
|
|
- printf(_("\nby Dave Coffin, dcoffin a cybercom o net\n"));
|
|
- printf(_("\nUsage: %s [OPTION]... [FILE]...\n\n"), argv[0]);
|
|
- puts(_("-v Print verbose messages"));
|
|
- puts(_("-c Write image data to standard output"));
|
|
- puts(_("-e Extract embedded thumbnail image"));
|
|
- puts(_("-i Identify files without decoding them"));
|
|
- puts(_("-i -v Identify files and show metadata"));
|
|
- puts(_("-z Change file dates to camera timestamp"));
|
|
- puts(_("-w Use camera white balance, if possible"));
|
|
- puts(_("-a Average the whole image for white balance"));
|
|
- puts(_("-A <x y w h> Average a grey box for white balance"));
|
|
- puts(_("-r <r g b g> Set custom white balance"));
|
|
- puts(_("+M/-M Use/don't use an embedded color matrix"));
|
|
- puts(_("-C <r b> Correct chromatic aberration"));
|
|
- puts(_("-P <file> Fix the dead pixels listed in this file"));
|
|
- puts(_("-K <file> Subtract dark frame (16-bit raw PGM)"));
|
|
- puts(_("-k <num> Set the darkness level"));
|
|
- puts(_("-S <num> Set the saturation level"));
|
|
- puts(_("-n <num> Set threshold for wavelet denoising"));
|
|
- puts(_("-H [0-9] Highlight mode (0=clip, 1=unclip, 2=blend, 3+=rebuild)"));
|
|
- puts(_("-t [0-7] Flip image (0=none, 3=180, 5=90CCW, 6=90CW)"));
|
|
- puts(_("-o [0-6] Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ,ACES)"));
|
|
-#ifndef NO_LCMS
|
|
- puts(_("-o <file> Apply output ICC profile from file"));
|
|
- puts(_("-p <file> Apply camera ICC profile from file or \"embed\""));
|
|
-#endif
|
|
- puts(_("-d Document mode (no color, no interpolation)"));
|
|
- puts(_("-D Document mode without scaling (totally raw)"));
|
|
- puts(_("-j Don't stretch or rotate raw pixels"));
|
|
- puts(_("-W Don't automatically brighten the image"));
|
|
- puts(_("-b <num> Adjust brightness (default = 1.0)"));
|
|
- puts(_("-g <p ts> Set custom gamma curve (default = 2.222 4.5)"));
|
|
- puts(_("-q [0-3] Set the interpolation quality"));
|
|
- puts(_("-h Half-size color image (twice as fast as \"-q 0\")"));
|
|
- puts(_("-f Interpolate RGGB as four colors"));
|
|
- puts(_("-m <num> Apply a 3x3 median filter to R-G and B-G"));
|
|
- puts(_("-s [0..N-1] Select one raw image or \"all\" from each file"));
|
|
- puts(_("-6 Write 16-bit instead of 8-bit"));
|
|
- puts(_("-4 Linear 16-bit, same as \"-6 -W -g 1 1\""));
|
|
- puts(_("-T Write TIFF instead of PPM"));
|
|
- puts("");
|
|
- return 1;
|
|
- }
|
|
- argv[argc] = "";
|
|
- for (arg=1; (((opm = argv[arg][0]) - 2) | 2) == '+'; ) {
|
|
- opt = argv[arg++][1];
|
|
- if ((cp = (char *) strchr (sp="nbrkStqmHACg", opt)))
|
|
- for (i=0; i < "114111111422"[cp-sp]-'0'; i++)
|
|
- if (!isdigit(argv[arg+i][0])) {
|
|
- fprintf (stderr,_("Non-numeric argument to \"-%c\"\n"), opt);
|
|
- return 1;
|
|
- }
|
|
- switch (opt) {
|
|
- case 'n': threshold = atof(argv[arg++]); break;
|
|
- case 'b': bright = atof(argv[arg++]); break;
|
|
- case 'r':
|
|
- FORC4 user_mul[c] = atof(argv[arg++]); break;
|
|
- case 'C': aber[0] = 1 / atof(argv[arg++]);
|
|
- aber[2] = 1 / atof(argv[arg++]); break;
|
|
- case 'g': gamm[0] = atof(argv[arg++]);
|
|
- gamm[1] = atof(argv[arg++]);
|
|
- if (gamm[0]) gamm[0] = 1/gamm[0]; break;
|
|
- case 'k': user_black = atoi(argv[arg++]); break;
|
|
- case 'S': user_sat = atoi(argv[arg++]); break;
|
|
- case 't': user_flip = atoi(argv[arg++]); break;
|
|
- case 'q': user_qual = atoi(argv[arg++]); break;
|
|
- case 'm': med_passes = atoi(argv[arg++]); break;
|
|
- case 'H': highlight = atoi(argv[arg++]); break;
|
|
- case 's':
|
|
- shot_select = abs(atoi(argv[arg]));
|
|
- multi_out = !strcmp(argv[arg++],"all");
|
|
- break;
|
|
- case 'o':
|
|
- if (isdigit(argv[arg][0]) && !argv[arg][1])
|
|
- output_color = atoi(argv[arg++]);
|
|
-#ifndef NO_LCMS
|
|
- else out_profile = argv[arg++];
|
|
- break;
|
|
- case 'p': cam_profile = argv[arg++];
|
|
-#endif
|
|
- break;
|
|
- case 'P': bpfile = argv[arg++]; break;
|
|
- case 'K': dark_frame = argv[arg++]; break;
|
|
- case 'z': timestamp_only = 1; break;
|
|
- case 'e': thumbnail_only = 1; break;
|
|
- case 'i': identify_only = 1; break;
|
|
- case 'c': write_to_stdout = 1; break;
|
|
- case 'v': verbose = 1; break;
|
|
- case 'h': half_size = 1; break;
|
|
- case 'f': four_color_rgb = 1; break;
|
|
- case 'A': FORC4 greybox[c] = atoi(argv[arg++]);
|
|
- case 'a': use_auto_wb = 1; break;
|
|
- case 'w': use_camera_wb = 1; break;
|
|
- case 'M': use_camera_matrix = 3 * (opm == '+'); break;
|
|
- case 'I': read_from_stdin = 1; break;
|
|
- case 'E': document_mode++;
|
|
- case 'D': document_mode++;
|
|
- case 'd': document_mode++;
|
|
- case 'j': use_fuji_rotate = 0; break;
|
|
- case 'W': no_auto_bright = 1; break;
|
|
- case 'T': output_tiff = 1; break;
|
|
- case '4': gamm[0] = gamm[1] =
|
|
- no_auto_bright = 1;
|
|
- case '6': output_bps = 16; break;
|
|
- default:
|
|
- fprintf (stderr,_("Unknown option \"-%c\".\n"), opt);
|
|
- return 1;
|
|
- }
|
|
- }
|
|
- if (arg == argc) {
|
|
- fprintf (stderr,_("No files to process.\n"));
|
|
- return 1;
|
|
- }
|
|
- if (write_to_stdout) {
|
|
- if (isatty(1)) {
|
|
- fprintf (stderr,_("Will not write an image to the terminal!\n"));
|
|
- return 1;
|
|
- }
|
|
-#if defined(WIN32) || defined(DJGPP) || defined(__CYGWIN__)
|
|
- if (setmode(1,O_BINARY) < 0) {
|
|
- perror ("setmode()");
|
|
- return 1;
|
|
- }
|
|
-#endif
|
|
- }
|
|
- for ( ; arg < argc; arg++) {
|
|
- status = 1;
|
|
- raw_image = 0;
|
|
- image = 0;
|
|
- oprof = 0;
|
|
- meta_data = ofname = 0;
|
|
- ofp = stdout;
|
|
- if (setjmp (failure)) {
|
|
- if (fileno(ifp) > 2) fclose(ifp);
|
|
- if (fileno(ofp) > 2) fclose(ofp);
|
|
- status = 1;
|
|
- goto cleanup;
|
|
- }
|
|
- ifname = argv[arg];
|
|
- if (!(ifp = fopen (ifname, "rb"))) {
|
|
- perror (ifname);
|
|
- continue;
|
|
- }
|
|
- status = (identify(),!is_raw);
|
|
- if (user_flip >= 0)
|
|
- flip = user_flip;
|
|
- switch ((flip+3600) % 360) {
|
|
- case 270: flip = 5; break;
|
|
- case 180: flip = 3; break;
|
|
- case 90: flip = 6;
|
|
- }
|
|
- if (timestamp_only) {
|
|
- if ((status = !timestamp))
|
|
- fprintf (stderr,_("%s has no timestamp.\n"), ifname);
|
|
- else if (identify_only)
|
|
- printf ("%10ld%10d %s\n", (long) timestamp, shot_order, ifname);
|
|
- else {
|
|
- if (verbose)
|
|
- fprintf (stderr,_("%s time set to %d.\n"), ifname, (int) timestamp);
|
|
- ut.actime = ut.modtime = timestamp;
|
|
- utime (ifname, &ut);
|
|
- }
|
|
- goto next;
|
|
- }
|
|
- write_fun = &CLASS write_ppm_tiff;
|
|
- if (thumbnail_only) {
|
|
- if ((status = !thumb_offset)) {
|
|
- fprintf (stderr,_("%s has no thumbnail.\n"), ifname);
|
|
- goto next;
|
|
- } else if (thumb_load_raw) {
|
|
- load_raw = thumb_load_raw;
|
|
- data_offset = thumb_offset;
|
|
- height = thumb_height;
|
|
- width = thumb_width;
|
|
- filters = 0;
|
|
- colors = 3;
|
|
- } else {
|
|
- fseek (ifp, thumb_offset, SEEK_SET);
|
|
- write_fun = write_thumb;
|
|
- goto thumbnail;
|
|
- }
|
|
- }
|
|
- if (load_raw == &CLASS kodak_ycbcr_load_raw) {
|
|
- height += height & 1;
|
|
- width += width & 1;
|
|
- }
|
|
- if (identify_only && verbose && make[0]) {
|
|
- printf (_("\nFilename: %s\n"), ifname);
|
|
- printf (_("Timestamp: %s"), ctime(×tamp));
|
|
- printf (_("Camera: %s %s\n"), make, model);
|
|
- if (artist[0])
|
|
- printf (_("Owner: %s\n"), artist);
|
|
- if (dng_version) {
|
|
- printf (_("DNG Version: "));
|
|
- for (i=24; i >= 0; i -= 8)
|
|
- printf ("%d%c", dng_version >> i & 255, i ? '.':'\n');
|
|
- }
|
|
- printf (_("ISO speed: %d\n"), (int) iso_speed);
|
|
- printf (_("Shutter: "));
|
|
- if (shutter > 0 && shutter < 1)
|
|
- shutter = (printf ("1/"), 1 / shutter);
|
|
- printf (_("%0.1f sec\n"), shutter);
|
|
- printf (_("Aperture: f/%0.1f\n"), aperture);
|
|
- printf (_("Focal length: %0.1f mm\n"), focal_len);
|
|
- printf (_("Embedded ICC profile: %s\n"), profile_length ? _("yes"):_("no"));
|
|
- printf (_("Number of raw images: %d\n"), is_raw);
|
|
- if (pixel_aspect != 1)
|
|
- printf (_("Pixel Aspect Ratio: %0.6f\n"), pixel_aspect);
|
|
- if (thumb_offset)
|
|
- printf (_("Thumb size: %4d x %d\n"), thumb_width, thumb_height);
|
|
- printf (_("Full size: %4d x %d\n"), raw_width, raw_height);
|
|
- } else if (!is_raw)
|
|
- fprintf (stderr,_("Cannot decode file %s\n"), ifname);
|
|
- if (!is_raw) goto next;
|
|
- shrink = filters && (half_size || (!identify_only &&
|
|
- (threshold || aber[0] != 1 || aber[2] != 1)));
|
|
- iheight = (height + shrink) >> shrink;
|
|
- iwidth = (width + shrink) >> shrink;
|
|
- if (identify_only) {
|
|
- if (verbose) {
|
|
- if (document_mode == 3) {
|
|
- top_margin = left_margin = fuji_width = 0;
|
|
- height = raw_height;
|
|
- width = raw_width;
|
|
- }
|
|
- iheight = (height + shrink) >> shrink;
|
|
- iwidth = (width + shrink) >> shrink;
|
|
- if (use_fuji_rotate) {
|
|
- if (fuji_width) {
|
|
- fuji_width = (fuji_width - 1 + shrink) >> shrink;
|
|
- iwidth = fuji_width / sqrt(0.5);
|
|
- iheight = (iheight - fuji_width) / sqrt(0.5);
|
|
- } else {
|
|
- if (pixel_aspect < 1) iheight = iheight / pixel_aspect + 0.5;
|
|
- if (pixel_aspect > 1) iwidth = iwidth * pixel_aspect + 0.5;
|
|
- }
|
|
- }
|
|
- if (flip & 4)
|
|
- SWAP(iheight,iwidth);
|
|
- printf (_("Image size: %4d x %d\n"), width, height);
|
|
- printf (_("Output size: %4d x %d\n"), iwidth, iheight);
|
|
- printf (_("Raw colors: %d"), colors);
|
|
- if (filters) {
|
|
- int fhigh = 2, fwide = 2;
|
|
- if ((filters ^ (filters >> 8)) & 0xff) fhigh = 4;
|
|
- if ((filters ^ (filters >> 16)) & 0xffff) fhigh = 8;
|
|
- if (filters == 1) fhigh = fwide = 16;
|
|
- if (filters == 9) fhigh = fwide = 6;
|
|
- printf (_("\nFilter pattern: "));
|
|
- for (i=0; i < fhigh; i++)
|
|
- for (c = i && putchar('/') && 0; c < fwide; c++)
|
|
- putchar (cdesc[fcol(i,c)]);
|
|
- }
|
|
- printf (_("\nDaylight multipliers:"));
|
|
- FORCC printf (" %f", pre_mul[c]);
|
|
- if (cam_mul[0] > 0) {
|
|
- printf (_("\nCamera multipliers:"));
|
|
- FORC4 printf (" %f", cam_mul[c]);
|
|
- }
|
|
- putchar ('\n');
|
|
- } else
|
|
- printf (_("%s is a %s %s image.\n"), ifname, make, model);
|
|
-next:
|
|
- fclose(ifp);
|
|
- continue;
|
|
- }
|
|
- if (meta_length) {
|
|
- meta_data = (char *) malloc (meta_length);
|
|
- merror (meta_data, "main()");
|
|
- }
|
|
- if (filters || colors == 1) {
|
|
- raw_image = (ushort *) calloc ((raw_height+7), raw_width*2);
|
|
- merror (raw_image, "main()");
|
|
- } else {
|
|
- image = (ushort (*)[4]) calloc (iheight, iwidth*sizeof *image);
|
|
- merror (image, "main()");
|
|
- }
|
|
- if (verbose)
|
|
- fprintf (stderr,_("Loading %s %s image from %s ...\n"),
|
|
- make, model, ifname);
|
|
- if (shot_select >= is_raw)
|
|
- fprintf (stderr,_("%s: \"-s %d\" requests a nonexistent image!\n"),
|
|
- ifname, shot_select);
|
|
- fseeko (ifp, data_offset, SEEK_SET);
|
|
- if (raw_image && read_from_stdin)
|
|
- fread (raw_image, 2, raw_height*raw_width, stdin);
|
|
- else (*load_raw)();
|
|
- if (document_mode == 3) {
|
|
- top_margin = left_margin = fuji_width = 0;
|
|
- height = raw_height;
|
|
- width = raw_width;
|
|
- }
|
|
- iheight = (height + shrink) >> shrink;
|
|
- iwidth = (width + shrink) >> shrink;
|
|
- if (raw_image) {
|
|
- image = (ushort (*)[4]) calloc (iheight, iwidth*sizeof *image);
|
|
- merror (image, "main()");
|
|
- crop_masked_pixels();
|
|
- free (raw_image);
|
|
- }
|
|
- if (zero_is_bad) remove_zeroes();
|
|
- bad_pixels (bpfile);
|
|
- if (dark_frame) subtract (dark_frame);
|
|
- quality = 2 + !fuji_width;
|
|
- if (user_qual >= 0) quality = user_qual;
|
|
- i = cblack[3];
|
|
- FORC3 if (i > cblack[c]) i = cblack[c];
|
|
- FORC4 cblack[c] -= i;
|
|
- black += i;
|
|
- i = cblack[6];
|
|
- FORC (cblack[4] * cblack[5])
|
|
- if (i > cblack[6+c]) i = cblack[6+c];
|
|
- FORC (cblack[4] * cblack[5])
|
|
- cblack[6+c] -= i;
|
|
- black += i;
|
|
- if (user_black >= 0) black = user_black;
|
|
- FORC4 cblack[c] += black;
|
|
- if (user_sat > 0) maximum = user_sat;
|
|
-#ifdef COLORCHECK
|
|
- colorcheck();
|
|
-#endif
|
|
- if (is_foveon) {
|
|
- if (document_mode || load_raw == &CLASS foveon_dp_load_raw) {
|
|
- for (i=0; i < height*width*4; i++)
|
|
- if ((short) image[0][i] < 0) image[0][i] = 0;
|
|
- } else foveon_interpolate();
|
|
- } else if (document_mode < 2)
|
|
- scale_colors();
|
|
- pre_interpolate();
|
|
- if (filters && !document_mode) {
|
|
- if (quality == 0)
|
|
- lin_interpolate();
|
|
- else if (quality == 1 || colors > 3)
|
|
- vng_interpolate();
|
|
- else if (quality == 2 && filters > 1000)
|
|
- ppg_interpolate();
|
|
- else if (filters == 9)
|
|
- xtrans_interpolate (quality*2-3);
|
|
- else
|
|
- ahd_interpolate();
|
|
- }
|
|
- if (mix_green)
|
|
- for (colors=3, i=0; i < height*width; i++)
|
|
- image[i][1] = (image[i][1] + image[i][3]) >> 1;
|
|
- if (!is_foveon && colors == 3) median_filter();
|
|
- if (!is_foveon && highlight == 2) blend_highlights();
|
|
- if (!is_foveon && highlight > 2) recover_highlights();
|
|
- if (use_fuji_rotate) fuji_rotate();
|
|
-#ifndef NO_LCMS
|
|
- if (cam_profile) apply_profile (cam_profile, out_profile);
|
|
-#endif
|
|
- convert_to_rgb();
|
|
- if (use_fuji_rotate) stretch();
|
|
-thumbnail:
|
|
- if (write_fun == &CLASS jpeg_thumb)
|
|
- write_ext = ".jpg";
|
|
- else if (output_tiff && write_fun == &CLASS write_ppm_tiff)
|
|
- write_ext = ".tiff";
|
|
- else
|
|
- write_ext = ".pgm\0.ppm\0.ppm\0.pam" + colors*5-5;
|
|
- ofname = (char *) malloc (strlen(ifname) + 64);
|
|
- merror (ofname, "main()");
|
|
- if (write_to_stdout)
|
|
- strcpy (ofname,_("standard output"));
|
|
- else {
|
|
- strcpy (ofname, ifname);
|
|
- if ((cp = strrchr (ofname, '.'))) *cp = 0;
|
|
- if (multi_out)
|
|
- sprintf (ofname+strlen(ofname), "_%0*d",
|
|
- snprintf(0,0,"%d",is_raw-1), shot_select);
|
|
- if (thumbnail_only)
|
|
- strcat (ofname, ".thumb");
|
|
- strcat (ofname, write_ext);
|
|
- ofp = fopen (ofname, "wb");
|
|
- if (!ofp) {
|
|
- status = 1;
|
|
- perror (ofname);
|
|
- goto cleanup;
|
|
- }
|
|
- }
|
|
- if (verbose)
|
|
- fprintf (stderr,_("Writing data to %s ...\n"), ofname);
|
|
- (*write_fun)();
|
|
- fclose(ifp);
|
|
- if (ofp != stdout) fclose(ofp);
|
|
-cleanup:
|
|
- if (meta_data) free (meta_data);
|
|
- if (ofname) free (ofname);
|
|
- if (oprof) free (oprof);
|
|
- if (image) free (image);
|
|
- if (multi_out) {
|
|
- if (++shot_select < is_raw) arg--;
|
|
- else shot_select = 0;
|
|
- }
|
|
- }
|
|
- return status;
|
|
-}
|
|
+/* RT: Delete from here */
|
|
+/*RT*/#undef SQR
|
|
+/*RT*/#undef MAX
|
|
+/*RT*/#undef MIN
|
|
+/*RT*/#undef ABS
|
|
+/*RT*/#undef LIM
|
|
+/*RT*/#undef ULIM
|
|
+/*RT*/#undef CLIP
|