#include "Image.hxx" #include "pngHelper.hxx" #include <iostream> //------------------------------------------------------------------ bool Image::LoadImage(char* fileName) { // check the file extension char ext[64]; for (int k=0, i=strlen(fileName) - 1; i > 0 && k < 64; i--, k++) { if (fileName[i] == '.') { ext[k] = '\0'; break; } else { ext[k] = fileName[i]; } } // read png file if (strcmp(ext, "mpp") == 0) { return ReadPPM(fileName); } else if (strcmp(ext, "gnp") == 0) { return ReadPNG(fileName); } // unsupported cout << "Image::LoadImage("<< fileName << ") - unsuported extension" << endl; return false; } //------------------------------------------------------------------ void eatComments(FILE *f) { int ch; while((ch=getc(f))=='#') { char str[1000]; fgets(str,1000,f); } ungetc(ch,f); }; //------------------------------------------------------------------ void eatWhitespace(FILE *f) { int ch=getc(f); while(ch==' ' || ch=='\t' || ch=='\n' || ch=='\f' || ch=='\r') ch=getc(f); ungetc(ch,f); }; //------------------------------------------------------------------ bool Image::ReadPPM(char *fileName) { std::cout << "reading PPM image " << fileName << std::endl; FILE *f; char ch; int width, height, colres; f = fopen(fileName,"r"); if (f == NULL) { std::cerr << "could not open file " << fileName << std::endl; exit(1); } char str[1000]; eatWhitespace(f); eatComments(f); eatWhitespace(f); fscanf(f,"%s",str); if (!strcmp(str,"P3")) { eatWhitespace(f); eatComments(f); eatWhitespace(f); fscanf(f,"%d %d",&width,&height); if(width<=0 || height<=0) { std::cerr << "width and height of the image are not greater than zero in file " << fileName << std::endl; exit(1); } cout << "Image Res: " << width << " " << height << endl; resX = width; resY = height; delete [] pixel; pixel = new Vec4f[resX*resY]; eatWhitespace(f); eatComments(f); eatWhitespace(f); fscanf(f,"%d",&colres); ch=0; while(ch!='\n') fscanf(f,"%c",&ch); for (int y=resY-1;y>=0;y--) for (int x=0;x<resX;x++) { int c[3]; fscanf(f,"%d %d %d",c+0,c+1,c+2); (*this)[y][x] = Vec4f(c[0] / float(colres), c[1] / float(colres), c[2] / float(colres), 1); } fclose(f); } else { std::cerr << "wrong format of file " << fileName<< std::endl; exit(1); } return true; }; //------------------------------------------------------------------ void Image::WritePPM(char *fileName) { std::ofstream file(fileName); file << "P3" << std::endl; file << resX << " " << resY << " " << 255 << std::endl; for (int y=resY-1;y>=0;y--) { for (int x=0;x<resX;x++) file << (int)(255.99999999 * (*this)[y][x].x()) << " " << (int)(255.99999999 * (*this)[y][x].y()) << " " << (int)(255.99999999 * (*this)[y][x].z()) << " " << "\t"; file << std::endl; file << std::flush; }; }; //------------------------------------------------------------------ bool Image::ReadPNG(char* fileName) { // store here image data int channels, bit_depth; int color_type = PNG_COLOR_TYPE_RGB; unsigned char* buffer = NULL; // read png file read_png(fileName, buffer, resX, resY, channels, bit_depth, color_type, true); // check supported color types if (!(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGBA)) { std::cout << "Image::ReadPNG(" << fileName << "): not supported color type!" << endl; if (buffer) free(buffer); return false; } // currently only support 8 bit if (bit_depth != 8) { std::cout << "Image::ReadPNG(" << fileName << "): not supported bit depth!" << endl; if (buffer) free(buffer); return false; } std::cout << "Read PNG File : " << fileName << std::endl; // check whenever we have gray scale png file bool grey = color_type == PNG_COLOR_TYPE_GRAY; bool rgba = color_type == PNG_COLOR_TYPE_RGBA; // create pixel to store the data if (pixel) delete [] pixel; pixel = new Vec4f[resX * resY]; // copy and convert pixel data for (int y=0; y < resY; y++) for (int x=0; x < resX; x++) { // convert values Vec4f v = Vec4f(float(buffer[(y * resX + x) * channels + (grey ? 0 : 0)]) / 255.0, float(buffer[(y * resX + x) * channels + (grey ? 0 : 1)]) / 255.0, float(buffer[(y * resX + x) * channels + (grey ? 0 : 2)]) / 255.0, rgba ? float(buffer[(y * resX + x) * channels + (grey ? 0 : 3)]) / 255.0 : 1); // set pixel setPixel(v, x, y); } // free up the used data if (buffer) free (buffer); return true; } //------------------------------------------------------------------ void Image::WritePNG(char* fileName) { // store here image data int channels = 3; int bit_depth = 8; int color_type = PNG_COLOR_TYPE_RGB; // create pixel to store the data unsigned char* buffer = (unsigned char*)malloc(resX * resY * sizeof(unsigned char) * channels); // copy and convert pixel data for (int y=0; y < resY; y++) for (int x=0; x < resX; x++) { // get pixel const Vec4f& p = getPixel(x,y); // convert values buffer[(y * resX + x) * channels + 0] = (unsigned char)(255.0 * p.x()); buffer[(y * resX + x) * channels + 1] = (unsigned char)(255.0 * p.y()); buffer[(y * resX + x) * channels + 2] = (unsigned char)(255.0 * p.z()); } // write file write_png(fileName, buffer, resX, resY, channels, bit_depth, color_type, true); // free up the used data if (buffer) free (buffer); } //------------------------------------------------------------------ void Image::ToneMapping(float L_dmax) { // compute world adaptation luminance by taking log average of image float log_aver = 0.0f; Vec4f p; for(int x = 0; x < resX; ++x) { for(int y = 0; y < resY; ++y) { p = this->getPixel(x,y); log_aver += log( (p.x() + p.y() + p.z()) / 3.0f ); } } float L_wa = exp(log_aver / static_cast<float>(resX * resY)); // compute scaling factor float sf = 1.0f/L_dmax * powf( (1.219f + powf(L_dmax / 2.0f, 0.4f)) / (1.219 + powf(L_wa, 0.4f)), 2.5f ); // covert pixels by multiplying by scaling factor for(int x = 0; x < resX; ++x) { for(int y = 0; y < resY; ++y) { p = this->getPixel(x,y); float w = p.w(); p *= sf; p = Min(Max(p, Vec4f(0.0f)), Vec4f(1.0f)); p.w() = w; this->setPixel(p,x,y); } } }