#include #include #include #include #include using std::string; using std::vector; using std::ostream; using std::istream; using std::ofstream; using std::ifstream; struct point{ public: point(); point(int _x, int _y); point(const int arr[2]); void operator=(const int arr[2]); void operator=(const point &other); friend ostream & operator<<(ostream &out, const point &p); friend istream & operator>>(istream &in, point &p); void SetX(int _x) {x = _x;} void SetY(int _y) {y = _y;} void Set(int _x, int _y) {x = _x; y = _y;} int GetX() const {return x;} int GetY() const {return y;} int & operator[](int index); const int & operator[](int index) const; bool operator==(const point &p) const; private: int x; // X value of point int y; // Y value of point }; struct pixel{ public: pixel(); pixel(const int arr[3]); pixel(int _r, int _g, int _b); void SwapChannels(int firstIndex, int secondIndex); void operator=(const int arr[3]); void operator=(const pixel &other); friend ostream & operator<<(ostream &out, const pixel &p); friend istream & operator>>(istream &in, pixel &p); void SetR(int _r) {r = _r;} void SetG(int _g) {g = _g;} void SetB(int _b) {b = _b;} void Set(int _r, int _g, int _b) {r = _r; g = _g; b = _b;} int GetR() const {return r;} int GetG() const {return g;} int GetB() const {return b;} int & operator[](int index); const int & operator[](int index) const; bool operator<(const pixel &other) const; bool operator<=(const pixel &other) const; bool operator>(const pixel &other) const; bool operator>=(const pixel &other) const; pixel operator+(const pixel &other) const; pixel operator-(const pixel &other) const; pixel & CapValuesLower(int lower); pixel & CapValuesUpper(int upper); pixel & CapValues(int lower, int upper); private: int r; // Red value of pixel int g; // Green value of pixel int b; // Blue value of pixel }; class ppmImage{ public: ppmImage(); ppmImage(const ppmImage &image); ppmImage(const string &_fileName); ppmImage(const point &_dimensions); ppmImage(int _dimensionX, int _dimensionY); ppmImage(const point &_dimensions, int _pixelValueLimit); ppmImage(int _dimensionX, int _dimensionY, int _pixelValueLimit); int Read(string _fileName); int Write(string _filename) const; void Resize(const int arr[2]); void Resize(const point &_dimensions); void Resize(int _dimensionX, int _dimensionY); point Size() const {return dimensions;} int GetPixelValueLimit() const {return pixelValueLimit;} void SetPixelValueLimit(int _pixelValueLimit) {pixelValueLimit = _pixelValueLimit;} int SwapPixelChannels(int swapChoice); vector & operator[](int index); const vector & operator[](int index) const; void operator=(const ppmImage &image); ppmImage operator+(const ppmImage &image); ppmImage operator-(const ppmImage &image); friend ostream & operator<<(ostream &out, const ppmImage &image); int & operator()(int row, int column, int pIndex); const int & operator()(int row, int column, int pIndex) const; private: point dimensions; // Point consisting of column and row dimensions of the image vector> pixels; // Vector consisting of vectors consisting of individiual pixels int pixelValueLimit; // Upper limit for pixel values void Resize(); }; int read_ppm(const string source_ppm_file_name, ppmImage &destination_object); int write_ppm(const string destination_ppm_file_name, const ppmImage &source_object); int swap_channels(ppmImage &image_object_to_be_modified, int swap_choice); ppmImage single_color(const ppmImage &source, int color_choice); int test_addition(const string filename_image1, const string filename_image2, const string filename_image3); int test_subtraction(const string filename_image1, const string filename_image2, const string filename_image3); int test_print(const string filename_image1); int main(int argc, char ** argv){ if(argc > 3 ){ // Execute if and only if there are at least four arguments (executable name, choice number, name of first and second file) int choiceNumber; // Argument number to execute corresponding process string ppmFileName1(argv[2]), ppmFileName2(argv[3]); // File names choiceNumber = std::stoi(argv[1]); // Convert argument to integer ppmImage readPPMImage; // Image of read file ppmImage processedPPMImage; // Image that is the result of process switch(choiceNumber){ case 1: if(argc != 5) return 0; test_addition(ppmFileName1, ppmFileName2, argv[4]); break; case 2: if(argc != 5) return 0; test_subtraction(ppmFileName1, ppmFileName2, argv[4]); break; case 3: read_ppm(ppmFileName1, readPPMImage); swap_channels(readPPMImage, 2); write_ppm(ppmFileName2, readPPMImage); break; case 4: read_ppm(ppmFileName1, readPPMImage); swap_channels(readPPMImage, 3); write_ppm(ppmFileName2, readPPMImage); break; case 5: read_ppm(ppmFileName1, readPPMImage); processedPPMImage = single_color(readPPMImage, 1); write_ppm(ppmFileName2, processedPPMImage); break; case 6: read_ppm(ppmFileName1, readPPMImage); processedPPMImage = single_color(readPPMImage, 2); write_ppm(ppmFileName2, processedPPMImage); break; case 7: read_ppm(ppmFileName1, readPPMImage); processedPPMImage = single_color(readPPMImage, 3); write_ppm(ppmFileName2, processedPPMImage); break; default: std::cout << "Invalid choice number!\n"; // Inform if invalid choice number is entered } } return 0; } int read_ppm(const string source_ppm_file_name, ppmImage &destination_object){ return destination_object.Read(source_ppm_file_name); // Execute Read function of the image with given file name } int write_ppm(const string destination_ppm_file_name, const ppmImage &source_object){ return source_object.Write(destination_ppm_file_name); // Execute Write function of the image with given file name } int swap_channels(ppmImage &image_object_to_be_modified, int swap_choice){ return image_object_to_be_modified.SwapPixelChannels(swap_choice); } ppmImage single_color(const ppmImage &source, int color_choice){ point dimensions = source.Size(); // Dimensions of the source image ppmImage copy(dimensions, source.GetPixelValueLimit()); // Initialized as blank image with dimensions and pixel value limit of source image for(int i = 0; i < dimensions.GetY(); ++i){ // For every row for(int j = 0; j < dimensions.GetX(); ++j){ // For every column copy(i, j, color_choice) = source(i, j, color_choice); // Set corresponding pixel value to source image's } } return copy; // Return new image } int test_addition(const string filename_image1, const string filename_image2, const string filename_image3){ ppmImage image1(filename_image1), image2(filename_image2); // Initialize images ppmImage sumImage = image1 + image2; // Sum them return write_ppm(filename_image3, sumImage); // Write sum image } int test_subtraction(const string filename_image1, const string filename_image2, const string filename_image3){ ppmImage image1(filename_image1), image2(filename_image2); // Initialize images ppmImage sumImage = image1 - image2; // Subtract them return write_ppm(filename_image3, sumImage); // Write result image } int test_print(const string filename_image1){ ppmImage image(filename_image1); // Initialize image std::cout << image; // Print it return 1; } point::point() : point(0, 0){} point::point(int _x, int _y) : x(_x), y(_y){} point::point(const int arr[2]) : point(arr[0], arr[1]){} void point::operator=(const int arr[2]){ x = arr[0]; y = arr[1]; } void point::operator=(const point &other){ x = other.x; y = other.y; } ostream & operator<<(ostream &out, const point &p){ out << p.x << " " << p.y; // Insert as "X Y" return out; } istream & operator>>(istream &in, point &p){ in >> p.x >> p.y; // Get first X value then Y value return in; } int & point::operator[](int index){ switch(index){ case 0: return x; case 1: return y; default: throw "Point index is out of bounds!"; } } const int & point::operator[](int index) const{ switch(index){ case 0: return x; case 1: return y; default: throw "Point index is out of bounds!"; } } bool point::operator==(const point &p) const{return x == p.x && y == p.y;} pixel::pixel() : pixel(0, 0, 0){} pixel::pixel(const int arr[3]) : pixel(arr[0], arr[1], arr[2]){} pixel::pixel(int _r, int _g, int _b) : r(_r), g(_g), b(_b){} void pixel::SwapChannels(int firstIndex, int secondIndex){ int temp = (*this)[firstIndex]; // Temporary variable to store pixel value at first index (*this)[firstIndex] = (*this)[secondIndex]; (*this)[secondIndex] = temp; } void pixel::operator=(const int arr[3]){ r = arr[0]; g = arr[1]; b = arr[2]; } void pixel::operator=(const pixel &other){ r = other.r; g = other.g; b = other.b; } ostream & operator<<(ostream &out, const pixel &p){ out << p.r << " " << p.g << " " << p.b; // Insert as "R G B" return out; } istream & operator>>(istream &in, pixel &p){ in >> p.r >> p.g >> p.b; // Get first R then G then B values return in; } int & pixel::operator[](int index){ switch(index){ case 0: return r; case 1: return g; case 2: return b; default: throw "Pixel index is out of bounds!"; } } const int & pixel::operator[](int index) const{ switch(index){ case 0: return r; case 1: return g; case 2: return b; default: throw "Pixel index is out of bounds!"; } } bool pixel::operator<(const pixel &other) const{ return r < other.r && g < other.g && b < other.b; } bool pixel::operator<=(const pixel &other) const{ return r <= other.r && g <= other.g && b <= other.b; } bool pixel::operator>(const pixel &other) const{ return r > other.r || g > other.g || b > other.b; } bool pixel::operator>=(const pixel &other) const{ return r >= other.r || g >= other.g || b >= other.b; } pixel pixel::operator+(const pixel &other) const{ return pixel(r + other.r, g + other.g, b + other.b); } pixel pixel::operator-(const pixel &other) const{ return pixel(r - other.r, g - other.g, b - other.b); } pixel & pixel::CapValuesLower(int lower){ r = r < lower ? lower : r; // If lower than lower limit than set to limit g = g < lower ? lower : g; // If lower than lower limit than set to limit b = b < lower ? lower : b; // If lower than lower limit than set to limit return *this; } pixel & pixel::CapValuesUpper(int upper){ r = r > upper ? upper : r; // If greater than upper limit than set to limit g = g > upper ? upper : g; // If greater than upper limit than set to limit b = b > upper ? upper : b; // If greater than upper limit than set to limit return *this; } pixel & pixel::CapValues(int lower, int upper){ CapValuesLower(lower); CapValuesUpper(upper); return *this; } ppmImage::ppmImage() : ppmImage(point(0, 0), 255){} ppmImage::ppmImage(const ppmImage &image) : ppmImage(image.dimensions, image.pixelValueLimit) { *this = image; // Use overloaded assignment operator to copy pixel values } ppmImage::ppmImage(const string &_fileName) : ppmImage(point(0, 0), 255) { Read(_fileName); // Read values from file } ppmImage::ppmImage(const point &_dimensions) : ppmImage(_dimensions, 255){} ppmImage::ppmImage(int _dimensionX, int _dimensionY) : ppmImage(point(_dimensionX, _dimensionY), 255){} ppmImage::ppmImage(const point &_dimensions, int _pixelValueLimit) : dimensions(_dimensions), pixels(), pixelValueLimit(_pixelValueLimit){Resize();} ppmImage::ppmImage(int _dimensionX, int _dimensionY, int _pixelValueLimit) : ppmImage(point(_dimensionX, _dimensionY), _pixelValueLimit){} int ppmImage::Read(string _fileName){ int readSuccesful = 0; // Is read successfull ifstream ppmFile(_fileName); // Open file char charBuffer; // Character buffer int intBuffer; // Integer Buffer ppmFile >> charBuffer; // Read first character if(charBuffer == 'P'){ // Continue only if it's 'P' ppmFile >> intBuffer; // Read first integer if(intBuffer == 3){ // Continue only if it's 3 ppmFile >> dimensions; // Get dimensions specified in the file pixels.resize(dimensions.GetY()); // Resize main vector (row vector) ppmFile >> pixelValueLimit; // Get limit for pixel values specified in the file pixel white(pixelValueLimit, pixelValueLimit, pixelValueLimit); // White pixel pixel black(0, 0, 0); // Black pixel pixel tempPixel; // Read pixel readSuccesful = 1; // Set to successful for now for(int i = 0; i < dimensions.GetY(); ++i){ // For every row pixels[i].resize(dimensions.GetX()); // Resize pixel vector (column vector) for(int j = 0; j < dimensions.GetX(); ++j){ // For every column ppmFile >> tempPixel; // Read pixel if(tempPixel > white || black > tempPixel || ppmFile.eof()){ readSuccesful = 0; // Read failed break; // Stop } pixels[i][j] = tempPixel; } if(!readSuccesful) break; } } } ppmFile.close(); // Close the file return readSuccesful; } int ppmImage::Write(string _filename) const{ int writeSuccessful = 1; // Is write successful ofstream ppmFile(_filename); // Open file ppmFile << *this; // This part changed due to PA3 ppmFile.close(); // Close file return writeSuccessful; } void ppmImage::Resize(const int arr[2]){ dimensions = arr; Resize(); // Resize to new dimensions } void ppmImage::Resize(const point &_dimensions){ dimensions = _dimensions; Resize(); // Resize to new dimensions } void ppmImage::Resize(int _dimensionX, int _dimensionY){ dimensions.Set(_dimensionX, _dimensionY); Resize(); // Resize to new dimensions } int ppmImage::SwapPixelChannels(int swapChoice){ int swapSuccesfull = 1; // Is process successful if(swapChoice > 0 && swapChoice < 4){ // Continue only if swap choice is 1, 2, or 3 for(int i = 0; i < dimensions.GetY(); ++i){ // For every row for(int j = 0; j < dimensions.GetX(); ++j){ // For every column int firstChannelIndex = swapChoice / 3 + 1; // One of the channel's index to be swapped int secondChannelIndex = swapChoice / 2 + 2; // Other's index int temp = (*this)(i, j, firstChannelIndex); (*this)(i, j, firstChannelIndex) = (*this)(i, j, secondChannelIndex); (*this)(i, j, secondChannelIndex) = temp; } } } return swapSuccesfull; } vector & ppmImage::operator[](int index){return pixels[index];} const vector & ppmImage::operator[](int index) const{return pixels[index];} void ppmImage::operator=(const ppmImage &image){ Resize(image.dimensions); // Resize to given image's dimensions pixelValueLimit = image.pixelValueLimit; for(int i = 0; i < dimensions.GetY(); ++i){ // For every row for(int j = 0; j < dimensions.GetX(); ++j){ // For every column pixels[i][j] = image[i][j]; // Copy the pixels respectively } } } void ppmImage::Resize(){ pixels.resize(dimensions.GetY()); // Resize vector of vectors of pixels to new row dimension for(int i = 0; i < dimensions.GetY(); ++i) // For evert vector of pixels pixels[i].resize(dimensions.GetX()); // Resize them to new column dimension } ppmImage ppmImage::operator+(const ppmImage &image){ ppmImage sumImage; // Sum of images if(dimensions == image.dimensions){ sumImage.Resize(dimensions); // Resize sum image to files' dimensions int pixelMaxValue = pixelValueLimit > image.pixelValueLimit ? pixelValueLimit : image.pixelValueLimit; sumImage.SetPixelValueLimit(pixelMaxValue); pixel sumPixel; // Sum of each pixel for(int i = 0; i < dimensions.GetY(); ++i){ for(int j = 0; j < dimensions.GetX(); ++j){ sumPixel = pixels[i][j] + image[i][j]; // Add two corresponding pixels together sumImage[i][j] = sumPixel.CapValuesUpper(pixelMaxValue); // Cap it under the max value, then set sum image's to it } } } return sumImage; } ppmImage ppmImage::operator-(const ppmImage &image){ ppmImage differenceImage; // Resulting image if(dimensions == image.dimensions){ differenceImage.Resize(dimensions); // Resize to the correct size int pixelMaxValue = pixelValueLimit > image.pixelValueLimit ? pixelValueLimit : image.pixelValueLimit; differenceImage.SetPixelValueLimit(pixelMaxValue); pixel differencePixel; // Difference of each pixel for(int i = 0; i < dimensions.GetY(); ++i){ for(int j = 0; j < dimensions.GetX(); ++j){ differencePixel = pixels[i][j] - image[i][j]; // Subtract pixels differenceImage[i][j] = differencePixel.CapValuesLower(0); // Make sure rgb values are greater than or equal to 0 } } } return differenceImage; } ostream & operator<<(ostream &out, const ppmImage &image){ out << "P3\n" << image.dimensions << std::endl << image.pixelValueLimit << std::endl; for(int i = 0; i < image.dimensions.GetY(); ++i){ for(int j = 0; j < image.dimensions.GetX(); ++j) out << image[i][j] << " "; out << std::endl; } return out; } int & ppmImage::operator()(int row, int column, int pIndex) {return pixels[row][column][pIndex - 1];} const int & ppmImage::operator()(int row, int column, int pIndex) const{ return pixels[row][column][pIndex - 1];}