#ifndef STIM_IMAGE_H #define STIM_IMAGE_H #ifdef USING_OPENCV #include #include #endif #include #include #include #include #include namespace stim{ /// This static class provides the STIM interface for loading, saving, and storing 2D images. /// Data is stored in an interleaved (BIP) format (default for saving and loading is RGB). //currently this interface uses CImg // T = data type (usually unsigned char) template class image{ T* img; //pointer to the image data (interleaved RGB for color) size_t R[3]; inline size_t X() const { return R[1]; } inline size_t Y() const { return R[2]; } inline size_t C() const { return R[0]; } void init(){ //initializes all variables, assumes no memory is allocated memset(R, 0, sizeof(size_t) * 3); //set the resolution and number of channels to zero img = NULL; } void unalloc(){ //frees any resources associated with the image if(img) free(img); //if memory has been allocated, free it } void clear(){ //clears all image data unalloc(); //unallocate previous memory init(); //re-initialize the variables } void allocate(){ unalloc(); img = (T*) malloc( sizeof(T) * R[0] * R[1] * R[2] ); //allocate memory } void allocate(size_t x, size_t y, size_t c){ //allocate memory based on the resolution R[0] = c; R[1] = x; R[2] = y; //set the resolution allocate(); //allocate memory } size_t bytes(){ return size() * sizeof(T); } inline size_t idx(size_t x, size_t y, size_t c = 0) const { return y * R[0] * R[1] + x * R[0] + c; } #ifdef USING_OPENCV int cv_type(){ if(typeid(T) == typeid(unsigned char)) return CV_MAKETYPE(CV_8U, (int)C()); if(typeid(T) == typeid(char)) return CV_MAKETYPE(CV_8S, (int)C()); if(typeid(T) == typeid(unsigned short)) return CV_MAKETYPE(CV_16U, (int)C()); if(typeid(T) == typeid(short)) return CV_MAKETYPE(CV_16S, (int)C()); if(typeid(T) == typeid(int)) return CV_MAKETYPE(CV_32S, (int)C()); if(typeid(T) == typeid(float)) return CV_MAKETYPE(CV_32F, (int)C()); if(typeid(T) == typeid(double)) return CV_MAKETYPE(CV_64F, (int)C()); std::cout<<"ERROR in stim::image::cv_type - no valid data type found"<& I){ init(); allocate(I.X(), I.Y(), I.C()); memcpy(img, I.img, bytes()); } /// Destructor - clear memory ~image(){ free(img); } ///Resize an image void resize(size_t x, size_t y, size_t c = 1) { allocate(x, y, c); } stim::image& operator=(const stim::image& I){ init(); if(&I == this) //handle self-assignment return *this; allocate(I.X(), I.Y(), I.C()); memcpy(img, I.img, bytes()); return *this; } //save a Netpbm file void load_netpbm(std::string filename) { std::ifstream infile(filename.c_str(), std::ios::in | std::ios::binary); //open an output file if (!infile) { std::cout << "Error opening input file in image::load_netpbm()" << std::endl; exit(1); } if (sizeof(T) != 1) { std::cout << "Error in image::load_netpbm() - data type must be 8-bit integer." << std::endl; exit(1); } size_t nc; //allocate space for the number of channels char format[2]; //allocate space to hold the image format tag infile.read(format, 2); //read the image format tag infile.seekg(1, std::ios::cur); //skip the newline character if (format[0] != 'P') { std::cout << "Error in image::load_netpbm() - file format tag is invalid: " << format[0] << format[1] << std::endl; exit(1); } if (format[1] == '5') nc = 1; //get the number of channels from the format flag else if (format[1] == '6') nc = 3; else { std::cout << "Error in image::load_netpbm() - file format tag is invalid: " << format[0] << format[1] << std::endl; exit(1); } unsigned char c; //stores a character while (infile.peek() == '#') { //if the next character indicates the start of a comment while (true) { c = infile.get(); if (c == 0x0A) break; } } std::string sw; //create a string to store the width of the image while(true){ c = infile.get(); //get a single character if (c == ' ') break; //exit if we've encountered a space sw.push_back(c); //push the character on to the string } size_t w = atoi(sw.c_str()); //convert the string into an integer std::string sh; while (true) { c = infile.get(); if (c == 0x0A) break; sh.push_back(c); } while (true) { //skip the maximum value c = infile.get(); if (c == 0x0A) break; } size_t h = atoi(sh.c_str()); //convert the string into an integer allocate(w, h, nc); //allocate space for the image infile.read((char*)img, size()); //copy the binary data from the file to the image infile.close(); } #ifdef USING_OPENCV void from_opencv(unsigned char* buffer, size_t width, size_t height) { allocate(width, height, 3); T value; size_t i; for (size_t c = 0; c < C(); c++) { //copy directly for (size_t y = 0; y < Y(); y++) { for (size_t x = 0; x < X(); x++) { i = y * X() * C() + x * C() + (2 - c); value = buffer[i]; img[idx(x, y, c)] = value; } } } } #endif /// Load an image from a file void load(std::string filename){ #ifdef USING_OPENCV cv::Mat cvImage = cv::imread(filename, CV_LOAD_IMAGE_UNCHANGED); //use OpenCV to open the image file if(!cvImage.data){ std::cout<<"ERROR stim::image::load() - unable to find image "< channel(size_t c) const { image r(X(), Y(), 1); //create a new image for(size_t x = 0; x < X(); x++){ for(size_t y = 0; y < Y(); y++){ r.img[r.idx(x, y, 0)] = img[idx(x, y, c)]; } } return r; } /// Returns an std::vector containing each channel as a separate image std::vector > split() const { std::vector > r; //create an image array r.resize(C()); //create images for each channel for (size_t c = 0; c < C(); c++) { //for each channel r[c] = channel(c); //copy the channel image to the array } return r; } /// Merge a series of single-channel images into a multi-channel image void merge(std::vector >& list) { size_t x = list[0].width(); //calculate the size of the image size_t y = list[0].height(); allocate(x, y, list.size()); //re-allocate the image for (size_t c = 0; c < list.size(); c++) //for each channel set_channel(list[c].channel(0).data(), c); //insert the channel into the output image } T& operator()(size_t x, size_t y, size_t c = 0){ return img[idx(x, y, c)]; } /// Set all elements in the image to a given scalar value /// @param v is the value used to set all values in the image image operator=(T v){ size_t N = size(); for(size_t n = 0; n < N; n++) img[n] = v; return *this; } /// Copy the given data to the specified channel /// @param c is the channel number that the data will be copied to /// @param buffer is a pointer to the image to be copied to channel c void set_channel(T* buffer, size_t c){ size_t x, y; for(y = 0; y < Y(); y++){ for(x = 0; x < X(); x++){ img[idx(x, y, c)] = buffer[y * X() + x]; } } } size_t channels() const{ return C(); } size_t width() const{ return X(); } size_t height() const{ return Y(); } T* data(){ return img; } //returns the size (number of values) of the image size_t size(){ return C() * X() * Y(); } /// Returns the number of nonzero values size_t nnz(){ size_t N = X() * Y() * C(); size_t nz = 0; for(size_t n = 0; n < N; n++) if(img[n] != 0) nz++; return nz; //return the number of nonzero pixels } //this function returns indices of pixels that have nonzero values std::vector sparse_idx(){ std::vector s; //allocate an array s.resize(nnz()); //allocate space in the array size_t N = size(); //size_t C = channels(); //T* ptr = img.data(); //get a pointer to the image data size_t i = 0; for(size_t n = 0; n < N; n++){ if(img[n] != 0){ s[i] = n; i++; } } return s; //return the index list } /// Returns the maximum pixel value in the image T maxv(){ T max_val = img[0]; //initialize the maximum value to the first one size_t N = size(); //get the number of pixels for (size_t n=0; n max_val){ //if the value is higher than the current max max_val = img[n]; } } return max_val; } /// Returns the maximum pixel value in the image T minv(){ T min_val = img[0]; //initialize the maximum value to the first one size_t N = size(); //get the number of pixels for (size_t n=0; n invert(T white_val){ size_t N = size(); //calculate the total number of values in the image image r(X(), Y(), C()); //allocate space for the resulting image for(size_t n = 0; n < N; n++) r.img[n] = white_val - img[n]; //perform the inversion return r; //return the inverted image } image srgb2lab(){ std::cout<<"ERROR stim::image::srgb2lab - function has been broken, re-implement."< convolve2(image mask){ std::cout<<"ERROR stim::image::convolve2 - function has been broken, and shouldn't really be in here."< rotate(float angle, float cx, float cy){ std::cout<<"ERROR stim::image::rotate - function has been broken, and shouldn't really be in here."< operator image() { image r(X(), Y(), C()); //create a new image std::copy(img, img + size(), r.data()); //copy and cast the data return r; //return the new image } }; }; //end namespace stim #endif