00001
00002
00003
00004
00005
00006
00007 #include <stdio.h>
00008 #include <stdlib.h>
00009 #include <tiffio.h>
00010 #include <assert.h>
00011 #include <ImfInputFile.h>
00012 #include <ImfChannelList.h>
00013 #include <ImfFrameBuffer.h>
00014 #include <half.h>
00015 #include <algorithm>
00016
00017 using std::min;
00018 using std::max;
00019
00020 using namespace Imf;
00021 using namespace Imath;
00022
00023 static bool ReadEXR(const char *name, float *&rgba, int &xRes, int &yRes, bool &hasAlpha);
00024 static void WriteTIFF(const char *name, float *rgba, int xRes, int yRes, bool hasAlpha);
00025
00026 static void usage() {
00027 fprintf( stderr, "usage: exrtotiff [options] <input.exr> <output.tiff>\n" );
00028 fprintf( stderr, "Supported options:\n");
00029 fprintf( stderr, "\t-scale scale\n" );
00030 fprintf( stderr, "\t-bloom\n" );
00031 fprintf( stderr, "\t-bloomscale scale [default: 0.1]\n" );
00032 fprintf( stderr, "\t-bloomradius radius [default: 0.01]\n" );
00033 fprintf( stderr, "\t-tonemap\n" );
00034 fprintf( stderr, "\t-repeatpix [count]\n" );
00035 fprintf( stderr, "\t-gamma gamma\n" );
00036 fprintf( stderr, "\t-bg bg-grey color\n");
00037 exit(1);
00038 }
00039
00040 void reinhard(float *d, int xRes, int yRes) {
00041 const float yw[3] = { 0.212671f, 0.715160f, 0.072169f };
00042
00043 float Ywa = 0.;
00044 for (int i = 0; i < xRes * yRes; ++i) {
00045 float y = yw[0] * d[3*i] + yw[1] * d[3*i+1] + yw[2] * d[3*i+2];
00046
00047
00048 if (y > 1e-4f) Ywa += logf(y);
00049 }
00050 Ywa = expf(Ywa / (xRes * yRes));
00051
00052
00053 float invY2 = 1.f / (Ywa * Ywa);
00054
00055
00056 for (int i = 0; i < xRes * yRes; ++i) {
00057 float y = yw[0] * d[3*i] + yw[1] * d[3*i+1] + yw[2] * d[3*i+2];
00058
00059 float s = (1.f + y * invY2) / (1.f + y);
00060
00061 d[3*i] *= s;
00062 d[3*i+1] *= s;
00063 d[3*i+2] *= s;
00064 }
00065 }
00066
00067 inline float Lerp(float t, float a, float b) {
00068 return (1.f - t) * a + t * b;
00069 }
00070
00071 void bloom(float *rgb, int xResolution, int yResolution, float bloomRadius,
00072 float bloomWeight) {
00073 int nPix = xResolution * yResolution;
00074
00075 if (bloomRadius > 0.f && bloomWeight > 0.f) {
00076
00077 int bloomSupport = int(ceilf(bloomRadius *
00078 max(xResolution, yResolution)));
00079 int bloomWidth = bloomSupport / 2;
00080
00081 float *bloomFilter = new float[bloomWidth * bloomWidth];
00082 for (int i = 0; i < bloomWidth * bloomWidth; ++i) {
00083 float dist = sqrtf(float(i)) / float(bloomWidth);
00084 bloomFilter[i] = powf(max(0.f, 1.f - dist), 4.f);
00085 }
00086
00087 float *bloomImage = new float[3*nPix];
00088 for (int y = 0; y < yResolution; ++y) {
00089 for (int x = 0; x < xResolution; ++x) {
00090
00091
00092 int x0 = max(0, x - bloomWidth);
00093 int x1 = min(x + bloomWidth, xResolution - 1);
00094 int y0 = max(0, y - bloomWidth);
00095 int y1 = min(y + bloomWidth, yResolution - 1);
00096 int offset = y * xResolution + x;
00097 float sumWt = 0.;
00098 for (int by = y0; by <= y1; ++by)
00099 for (int bx = x0; bx <= x1; ++bx) {
00100
00101 int dx = x - bx, dy = y - by;
00102 if (dx == 0 && dy == 0) continue;
00103 int dist2 = dx*dx + dy*dy;
00104 if (dist2 < bloomWidth * bloomWidth) {
00105 int bloomOffset = bx + by * xResolution;
00106 float wt = bloomFilter[dist2];
00107 sumWt += wt;
00108 for (int j = 0; j < 3; ++j)
00109 bloomImage[3*offset+j] += wt * rgb[3*bloomOffset+j];
00110 }
00111 }
00112 bloomImage[3*offset ] /= sumWt;
00113 bloomImage[3*offset+1] /= sumWt;
00114 bloomImage[3*offset+2] /= sumWt;
00115 }
00116 }
00117
00118 for (int i = 0; i < 3 * nPix; ++i)
00119 rgb[i] = Lerp(bloomWeight, rgb[i], bloomImage[i]);
00120
00121 delete[] bloomFilter;
00122 delete[] bloomImage;
00123 }
00124 }
00125
00126 int main(int argc, char *argv[])
00127 {
00128 float scale = 1.f, gamma = 2.2f;
00129 float bggray = -1.f;
00130 bool tonemap = false, bloom = false;
00131 float bloomRadius = .01f, bloomScale = .1f;
00132 int repeat = 1;
00133 float rp = 1;
00134
00135 int argNum = 1;
00136 while (argNum < argc && argv[argNum][0] == '-') {
00137 #define ARG(name, var) \
00138 else if (!strcmp(argv[argNum], "-" name)) { \
00139 if (argNum+1 == argc) \
00140 usage(); \
00141 var = atof(argv[argNum+1]); \
00142 ++argNum; \
00143 }
00144 if (!strcmp(argv[argNum], "-tonemap")) tonemap = true;
00145 else if (!strcmp(argv[argNum], "-bloom")) bloom = true;
00146 ARG("bloomscale", bloomScale)
00147 ARG("bloomradius", bloomRadius)
00148 ARG("repeatpix", rp)
00149 ARG("scale", scale)
00150 ARG("gamma", gamma)
00151 ARG("bg", bggray)
00152 else
00153 usage();
00154 ++argNum;
00155
00156 }
00157 if (argNum + 2 > argc) usage();
00158 repeat = int(rp);
00159
00160 char *inFile = argv[argNum], *outFile = argv[argNum+1];
00161 float *rgba;
00162 int xRes, yRes;
00163 bool hasAlpha;
00164
00165 if (ReadEXR(inFile, rgba, xRes, yRes, hasAlpha)) {
00166 if (repeat > 1) {
00167 assert(hasAlpha != 0);
00168 float *rscale = new float[4 * repeat * xRes * repeat * yRes];
00169 float *rsp = rscale;
00170 for (int y = 0; y < repeat * yRes; ++y) {
00171 int yy = y / repeat;
00172 for (int x = 0; x < repeat * xRes; ++x) {
00173 int xx = x / repeat;
00174 for (int c = 0; c < 4; ++c)
00175 *rsp++ = rgba[4 * (yy * xRes + xx) + c];
00176 }
00177 }
00178 xRes *= repeat;
00179 yRes *= repeat;
00180 rgba = rscale;
00181 }
00182
00183 float *rgb = new float[xRes*yRes*3];
00184 for (int i = 0; i < xRes*yRes; ++i) {
00185 for (int j = 0; j < 3; ++j) {
00186 rgb[3*i+j] = scale * rgba[4*i+j];
00187 if (bggray > 0)
00188 rgb[3*i+j] = rgba[4*i+3] * rgb[3*i+j] + (1.f - rgba[4*i+3]) * bggray;
00189 }
00190 if (bggray > 0)
00191 rgba[4*i+3] = 1.f;
00192 }
00193
00194 if (bloom) ::bloom(rgb, xRes, yRes, bloomRadius, bloomScale);
00195 if (tonemap) reinhard(rgb, xRes, yRes);
00196
00197 for (int i = 0; i < xRes*yRes; ++i) {
00198 float m = 0.f;
00199 for (int j = 0; j < 3; ++j) {
00200 rgba[4*i+j] = 255.f * powf(std::max(0.f, rgb[3*i+j]), 1.f / gamma);
00201 m = std::max(m, rgba[4*i+j]);
00202 }
00203 if (m > 255.f) {
00204 for (int j = 0; j < 3; ++j)
00205 rgba[4*i+j] = 255.f * (rgba[4*i+j] / m);
00206 }
00207
00208
00209
00210 rgba[4*i+3] *= 255.f;
00211 }
00212
00213 WriteTIFF(outFile, rgba, xRes, yRes, hasAlpha);
00214 }
00215 return 0;
00216 }
00217
00218 void WriteTIFF(const char *name, float *rgba, int XRes, int YRes, bool hasAlpha)
00219 {
00220
00221 TIFF *tiff = TIFFOpen(name, "w");
00222 if (!tiff) {
00223 fprintf(stderr, "Unable to open TIFF %s for writing", name);
00224 return;
00225 }
00226
00227 int nChannels = hasAlpha ? 4 : 3;
00228 TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, nChannels);
00229 if (hasAlpha) {
00230 short int extra[] = { EXTRASAMPLE_ASSOCALPHA };
00231 TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, (short)1, extra);
00232 }
00233
00234 TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, XRes);
00235 TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, YRes);
00236 TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8);
00237 TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
00238
00239 TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1);
00240 TIFFSetField(tiff, TIFFTAG_XRESOLUTION, 1.f);
00241 TIFFSetField(tiff, TIFFTAG_YRESOLUTION, 1.f);
00242 TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, (short)1);
00243 TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
00244 TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
00245 TIFFSetField(tiff, TIFFTAG_ORIENTATION, (int)ORIENTATION_TOPLEFT);
00246
00247 unsigned char *buf = new unsigned char[nChannels * XRes];
00248 for (int y = 0; y < YRes; ++y) {
00249 unsigned char *bufp = buf;
00250 for (int x = 0; x < XRes; ++x) {
00251
00252 for (int s = 0; s < nChannels; ++s)
00253 *bufp++ = (unsigned char)*rgba++;
00254 }
00255 TIFFWriteScanline(tiff, buf, y, 1);
00256 }
00257
00258 delete[] buf;
00259 TIFFClose(tiff);
00260 }
00261
00262 static bool ReadEXR(const char *name, float *&rgba, int &xRes, int &yRes, bool &hasAlpha)
00263 {
00264 InputFile file(name);
00265 Box2i dw = file.header().dataWindow();
00266 xRes = dw.max.x - dw.min.x + 1;
00267 yRes = dw.max.y - dw.min.y + 1;
00268
00269 half *hrgba = new half[4 * xRes * yRes];
00270
00271
00272 hasAlpha = true;
00273 int nChannels = 4;
00274
00275 half *hp = hrgba - nChannels * (dw.min.x + dw.min.y * xRes);
00276
00277 FrameBuffer frameBuffer;
00278 frameBuffer.insert("R", Slice(HALF, (char *)hp,
00279 4*sizeof(half), xRes * 4 * sizeof(half), 1, 1, 0.0));
00280 frameBuffer.insert("G", Slice(HALF, (char *)hp+sizeof(half),
00281 4*sizeof(half), xRes * 4 * sizeof(half), 1, 1, 0.0));
00282 frameBuffer.insert("B", Slice(HALF, (char *)hp+2*sizeof(half),
00283 4*sizeof(half), xRes * 4 * sizeof(half), 1, 1, 0.0));
00284 frameBuffer.insert("A", Slice(HALF, (char *)hp+3*sizeof(half),
00285 4*sizeof(half), xRes * 4 * sizeof(half), 1, 1, 1.0));
00286
00287 file.setFrameBuffer(frameBuffer);
00288 file.readPixels(dw.min.y, dw.max.y);
00289
00290 rgba = new float[nChannels * xRes * yRes];
00291 for (int i = 0; i < nChannels * xRes * yRes; ++i)
00292 rgba[i] = hrgba[i];
00293 delete[] hrgba;
00294
00295 return rgba;
00296 }