From 615f4d04888761365c8f415f9d2d0bff01e0d745 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Mon, 29 Jul 2024 15:00:45 +0300 Subject: [PATCH 01/17] upload files --- Image.cpp | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++ Image.h | 139 +++++++++++++++++++++++++++++++++ functions.cpp | 35 +++++++++ functions.h | 11 +++ 4 files changed, 397 insertions(+) create mode 100644 Image.cpp create mode 100644 Image.h create mode 100644 functions.cpp create mode 100644 functions.h diff --git a/Image.cpp b/Image.cpp new file mode 100644 index 0000000..ca8f5b8 --- /dev/null +++ b/Image.cpp @@ -0,0 +1,212 @@ +#include "Image.h" +using namespace std; + +tuple Image::getSize() +{ + return make_tuple(width, height); +} + +bool Image::empty() +{ + return width * height == 0 || data == nullptr; +} + +Image Image::clone()const +{ + vector res; + for (int i = 0; i < width * height * channels; i++) + { + res.push_back(data[i]); + } + Image m(width, height, channels, res); + return m; +} + +vector> Image::splitChannels()const +{ + vectorred; + vectorgreen; + vectorblue; + for (int index = 0; index < width * height * channels; index++) + { + if (index % 3 == 0) + red.push_back(data[index]); + else + { + if (index % 3 == 1) + green.push_back(data[index]); + else + blue.push_back(data[index]); + } + } + vector>res({ red,green,blue }); + return res; +} + +vector Image::getData() +{ + vectordata; + for (int i = 0; i < width * height * channels; i++) + { + data.push_back(this->data[i]); + } + return data; +} + +std::vector Image::at(int i, int j) { + if (i < 0 || i >= height || j < 0 || j >= width) { + throw std::out_of_range("Index out of range"); + } + + std::vector pixel(channels); + for (int c = 0; c < channels; ++c) { + pixel[c] = data[(i * width * channels) + (j * channels) + c]; + } + pixel[0] = 180; + return pixel; +} + +uint8_t Image::at(int i, int j, int c) { + if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); + return data[(i * width * channels) + (j * channels) + c]; +} + +uint8_t* Image::ptr(int i) +{ + if (i<0 || i>height) throw out_of_range("Index out of range"); + return data + i * width * channels; +} + +uint8_t* Image::ptr(int i, int j) +{ + if (i<0 || i>height || j<0 || j>width) throw out_of_range("Index out of range"); + return data + i * width * channels + j * channels; +} + +uint8_t* Image::ptr(int i, int j, int c) +{ + if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); + return data + i * width * channels + j * channels + c; +} + + +Image Image::operator+(uint8_t value) const +{ + Image m(width, height, channels); + for (int index = 0; index < width * height * channels; index++) + m.data[index] = data[index] + value; + return m; +} + +Image Image::operator-(uint8_t value) const +{ + Image m(width, height, channels); + for (int index = 0; index < width * height * channels; index++) + m.data[index] = data[index] - value; + return m; +} + +bool Image::operator==(Image const& other) const +{ + if (width != other.width || height != other.height || channels != other.channels) + throw exception("It is impossible to compare objects with different dimensions"); + for (int i = 0; i < width * height * channels; i++) + { + if (other.data[i] != data[i]) + return false; + } + return true; +} + +bool Image::operator==(uint8_t const& value) const +{ + for (int i = 0; i < width * height * channels; i++) + { + if (data[i] != value) + return false; + } + return true; +} + +Image Image::operator=(Image const& other) +{ + Image m; + m.width = other.width; + height = other.height; + ROI = other.ROI; + channels = other.channels; + data = other.data; + refCount = (other.refCount); + (*refCount)++; + return m; +} + +uint8_t& Image::operator()(int i, int j, int c) { + if (i < 0 || i >= height || j < 0 || j >= width || c < 0 || c >= channels) { + throw std::out_of_range("Index out of range"); + } + return data[(i * width * channels) + (j * channels) + c]; +} + +void Image::copy(Image img) +{ + if (width != img.width || height != img.height || ROI != img.ROI || channels != img.channels) + throw out_of_range("Index out of range"); + data = img.data; + (*img.refCount)++; + (*refCount)--; +} + +void Image::copy2(Image img) +{ + if (width != img.width || height != img.height || ROI != img.ROI || channels != img.channels) + throw out_of_range("Index out of range"); + for (int i = 0; i < width * height * channels; i++) + { + *(data + i) = *(img.data + i); + } +} + +void Image::waitKey(int delay) +{ + this_thread::sleep_for(std::chrono::seconds(delay)); +} + + +vector Image::getAllPixel(int i, int j) const { + if (i < 0 || i >= height || j < 0 || j >= width) { + throw std::out_of_range("Index out of range"); + } + + std::vector pixel(channels); + for (int c = 0; c < channels; ++c) { + pixel[c] = data[(i * width * channels) + (j * channels) + c]; + } + return pixel; +} + +uint8_t& Image::getPixelCH(int i, int j, int c) +{ + if (i < 0 || i >= height || j < 0 || j >= width || c < 0 || c >= channels) { + throw std::out_of_range("Index out of range"); + } + return data[(i * width * channels) + (j * channels) + c]; +} + +void Image::setAllPixel(int i, int j, const vector& pixel) +{ + + if (pixel.size() < 0 || pixel.size() > 3 || i < 0 || i >= height || j < 0 || j >= width) throw std::out_of_range("Index out of range"); + for (int c = 0; c < channels; c++) + { + setPixelCH(i, j, c, pixel.at(c)); + } +} + +void Image::setPixelCH(int i, int j, int c, int value) +{ + if (value < 0 || value>255) throw out_of_range("Index out of range"); + if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); + if (*refCount > 1) {}//function that nechami did + data[(i * width * channels) + (j * channels) + c] = value; +} \ No newline at end of file diff --git a/Image.h b/Image.h new file mode 100644 index 0000000..c95e9d3 --- /dev/null +++ b/Image.h @@ -0,0 +1,139 @@ +#pragma once +#include +#include +#include +#include +#include + +class Image +{ +public: + int width; + int height; + int ROI; + int* refCount; + int channels; + uint8_t* data; + + //defult c'tor + Image() + { + refCount = new int; + data = nullptr; + } + + //c'tor + Image(int w, int h, int c, std::vector const& _data = {}) + { + if (c != 1 && c != 3) + throw std::exception("The channels must be 1 or 3"); + if (w < 1 || h < 1) + throw std::exception("the arguments are invalid"); + width = w; + height = h; + channels = c; + refCount = new int; + *refCount = 1; + ROI = width; + data = new uint8_t[(w * 2) * h * c]; + if (!_data.empty()) + memcpy(data, _data.data(), (width + 2) * height * channels); + } + + //view c'tor + Image(Image const& other, int tl_x, int tl_y, int ROI_width, int ROI_height) + { + if (tl_x<0 || tl_y + ROI_width > other.ROI || tl_y<0 || tl_x + ROI_height>other.height) + throw std::invalid_argument("Bad view image dimension"); + width = ROI_width; + height = ROI_height; + ROI = other.ROI; + channels = other.channels; + data = other.data + tl_x * ROI + tl_y; + refCount = (other.refCount); + (*refCount)++; + } + + //copy c'tor + Image(Image const& other) + :width(other.width), height(other.height), ROI(other.ROI), channels(other.channels) + { + data = other.data; + refCount = (other.refCount); + (*refCount)++; + } + + //move c'tor + Image(Image&& other)noexcept : + width(other.width), height(other.height), ROI(other.ROI), channels(other.channels) + { + data = other.data; + other.data = nullptr; + refCount = (other.refCount); + other.refCount = nullptr; + } + + //d'tor + ~Image() + { + if (refCount) + { + if (*refCount == 1) + { + delete refCount; + delete[] data; + } + else + *refCount--; + } + } + + void copy/*AndWrite*/(Image img); + + void copy2/*AndWrite*/(Image img); + + static void waitKey(int delay = 0); + + std::tuple getSize(); + + + std::vector getAllPixel(int i, int j) const; + uint8_t& getPixelCH(int i, int j, int c); + void setAllPixel(int i, int j, const std::vector& pixel); + void setPixelCH(int i, int j, int c, int value); + + + bool empty(); + + Image clone()const; + + std::vector>splitChannels()const; + + std::vectorgetData(); + + uint8_t at(int i, int j, int c); + + std::vector at(int i, int j); + + + uint8_t* ptr(int i); + + uint8_t* ptr(int i, int j); + + uint8_t* ptr(int i, int j, int c); + + + Image operator=(Image const& other); + + uint8_t& operator()(int i, int j, int c); + + Image operator +(uint8_t value) const; // by element + + Image operator -(uint8_t value) const; // by element + + bool operator ==(Image const& other) const; // compare by element to other image + + bool operator ==(uint8_t const& value) const; // compare all elements to value + +}; + diff --git a/functions.cpp b/functions.cpp new file mode 100644 index 0000000..8b9bb45 --- /dev/null +++ b/functions.cpp @@ -0,0 +1,35 @@ +#include "functions.h" +using namespace std; +struct BMP { + int width; + int height; + vector pixels; +}; + +Image readImage(string filename) { + BMP image; + + ifstream file(filename, ios::binary); + if (!file.is_open()) { + throw runtime_error("Unable to open file"); + } + + file.seekg(18, ios::beg); + file.read(reinterpret_cast(&image.width), sizeof(image.width)); + file.read(reinterpret_cast(&image.height), sizeof(image.height)); + + file.seekg(54, ios::beg); // Skip to pixel data + + int rowSize = ((image.width * 3 + 3) / 4) * 4; // Calculate padded row size + image.pixels.resize(rowSize * image.height); + + for (int i = 0; i < image.height; i++) { + file.read(reinterpret_cast(&image.pixels[(image.height - 1 - i) * rowSize]), rowSize); + } + + file.close(); + + Image result(image.width, image.height, 3, image.pixels); + int p; + return result; +} \ No newline at end of file diff --git a/functions.h b/functions.h new file mode 100644 index 0000000..0f9670c --- /dev/null +++ b/functions.h @@ -0,0 +1,11 @@ +#pragma once +#include "Image.h" +#include +#include +#include +#include +Image readImage(std::string filename); + +void DisplayImage(const Image& img); + +void writeImage(const std::string& filename, const Image& image); \ No newline at end of file From d9d55eb8e109bda2bc9c6fcbd028c71199b6d2f3 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Tue, 30 Jul 2024 10:14:33 +0300 Subject: [PATCH 02/17] Update functions.cpp --- functions.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/functions.cpp b/functions.cpp index 8b9bb45..51f870c 100644 --- a/functions.cpp +++ b/functions.cpp @@ -32,4 +32,83 @@ Image readImage(string filename) { Image result(image.width, image.height, 3, image.pixels); int p; return result; -} \ No newline at end of file +} + +void writeImage(const std::string& filename, const Image& image) { + std::ofstream bmpFile(filename, std::ios::out | std::ios::binary); + if (!bmpFile) { + std::cerr << "Error opening file for writing." << std::endl; + return; + } + + int rowSize = image.width * 3; // Row size including padding + int padding = (4 - (rowSize % 4)) % 4; + int rowSizeWithPadding = rowSize + padding; + int fileSize = 54 + rowSizeWithPadding * image.height; // Total file size + int imageSize = rowSizeWithPadding * image.height; + + + bmpFile.put('B').put('M'); // BMP magic number + bmpFile.write(reinterpret_cast(&fileSize), 4); // File size + bmpFile.write("\0\0\0\0", 4); // Reserved + int offsetToData = 54; + bmpFile.write(reinterpret_cast(&offsetToData), 4); // Offset to image data + + // DIB Header (BITMAPINFOHEADER) + int headerSize = 40; + bmpFile.write(reinterpret_cast(&headerSize), 4); // DIB header size + bmpFile.write(reinterpret_cast(&image.width), 4); // Image width + bmpFile.write(reinterpret_cast(&image.height), 4); // Image height + short planes = 1; + bmpFile.write(reinterpret_cast(&planes), 2); // Planes + short bitsPerPixel = 24; + bmpFile.write(reinterpret_cast(&bitsPerPixel), 2); // Bits per pixel + int compression = 0; + bmpFile.write(reinterpret_cast(&compression), 4); // Compression method + bmpFile.write(reinterpret_cast(&imageSize), 4); // Image size + int resolutionX = 2835; // Pixels per meter (72 DPI) + int resolutionY = 2835; // Pixels per meter (72 DPI) + bmpFile.write(reinterpret_cast(&resolutionX), 4); // Horizontal resolution + bmpFile.write(reinterpret_cast(&resolutionY), 4); // Vertical resolution + int colorsUsed = 0; + bmpFile.write(reinterpret_cast(&colorsUsed), 4); // Colors in color table + int importantColors = 0; + bmpFile.write(reinterpret_cast(&importantColors), 4); // Important color count + + // Write image data (bottom to top) + for (int y = image.height-1; y >=0; y--) { + for (int x = 0; x < image.width; x++) { + int index = (y * image.width + x) * 3+padding*y; + bmpFile.write(reinterpret_cast(&image.data[index]), 1); // Blue channel + bmpFile.write(reinterpret_cast(&image.data[index + 1]), 1); // Green channel + bmpFile.write(reinterpret_cast(&image.data[index+2]), 1); // Red channel + } + // Write padding bytes + for (int p = 0; p < padding; p++) { + bmpFile.write("\0", 1); + } + } + + bmpFile.close(); +} + +void DisplayImage(const Image& img) { + BITMAPINFO bmi = { 0 }; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = img.width; + bmi.bmiHeader.biHeight = -img.height; // Minus height to display image right-side up + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; // Assuming 24-bit RGB image + bmi.bmiHeader.biCompression = BI_RGB; + //HWND console = GetConsoleWindow(); + //PAINTSTRUCT ps; + //HDC hdc = GetDC(console); + + HDC hdc = GetDC(NULL); + StretchDIBits(hdc, 0, 0, img.width, img.height, 0, 0, img.width, img.height, img.data, &bmi, DIB_RGB_COLORS, SRCCOPY); + //Sleep(30000); // Sleep for 30,000 milliseconds (30 seconds) + + ReleaseDC(NULL, hdc); + + //EndPaint(console, &ps); +} From 80a8005b48028a263278bbfa31ced7a6eeda3a77 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Tue, 30 Jul 2024 11:52:31 +0300 Subject: [PATCH 03/17] Update Image.h- add the 3 static functions- read, display and write --- Image.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Image.h b/Image.h index c95e9d3..b158726 100644 --- a/Image.h +++ b/Image.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include class Image { @@ -84,10 +86,18 @@ class Image delete[] data; } else - *refCount--; + (*refCount)--; } } + + static Image readImage(std::string filename); + + static void DisplayImage(const Image& img); + + static void writeImage(const std::string& filename, const Image& image); + + void copy/*AndWrite*/(Image img); void copy2/*AndWrite*/(Image img); From 2c4f70928050fde862054a690f7c861902b63c82 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Tue, 30 Jul 2024 11:54:32 +0300 Subject: [PATCH 04/17] Update Image.cpp- add the implement of the static functions write, read and display --- Image.cpp | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 2 deletions(-) diff --git a/Image.cpp b/Image.cpp index ca8f5b8..fcbd6f2 100644 --- a/Image.cpp +++ b/Image.cpp @@ -53,6 +53,7 @@ vector Image::getData() return data; } + std::vector Image::at(int i, int j) { if (i < 0 || i >= height || j < 0 || j >= width) { throw std::out_of_range("Index out of range"); @@ -148,6 +149,117 @@ uint8_t& Image::operator()(int i, int j, int c) { return data[(i * width * channels) + (j * channels) + c]; } + +Image Image::readImage(string filename) +{ + int width, height; + vector pixels; + + ifstream file(filename, ios::binary); + if (!file.is_open()) { + throw runtime_error("Unable to open file"); + } + + file.seekg(18, ios::beg); + file.read(reinterpret_cast(&width), sizeof(width)); + file.read(reinterpret_cast(&height), sizeof(height)); + + file.seekg(54, ios::beg); // Skip to pixel data + + int rowSize = ((width * 3 + 3) / 4) * 4; // Calculate padded row size + pixels.resize(rowSize * height); + + for (int i = 0; i < height; i++) { + file.read(reinterpret_cast(&pixels[(height - 1 - i) * rowSize]), rowSize); + } + + file.close(); + + Image result(width, height, 3, pixels); + return result; +} + +void Image::DisplayImage(const Image& img) +{ + BITMAPINFO bmi = { 0 }; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = img.width; + bmi.bmiHeader.biHeight = -img.height; // Minus height to display image right-side up + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; // Assuming 24-bit RGB image + bmi.bmiHeader.biCompression = BI_RGB; + //HWND console = GetConsoleWindow(); + //PAINTSTRUCT ps; + //HDC hdc = GetDC(console); + + HDC hdc = GetDC(NULL); + StretchDIBits(hdc, 0, 0, img.width, img.height, 0, 0, img.width, img.height, img.data, &bmi, DIB_RGB_COLORS, SRCCOPY); + //Sleep(30000); // Sleep for 30,000 milliseconds (30 seconds) + + ReleaseDC(NULL, hdc); + + //EndPaint(console, &ps); +} + +void Image::writeImage(const std::string& filename, const Image& image) +{ + std::ofstream bmpFile(filename, std::ios::out | std::ios::binary); + if (!bmpFile) { + std::cerr << "Error opening file for writing." << std::endl; + return; + } + + int rowSize = image.width * 3; // Row size including padding + int padding = (4 - (rowSize % 4)) % 4; + int rowSizeWithPadding = rowSize + padding; + int fileSize = 54 + rowSizeWithPadding * image.height; // Total file size + int imageSize = rowSizeWithPadding * image.height; + + + bmpFile.put('B').put('M'); // BMP magic number + bmpFile.write(reinterpret_cast(&fileSize), 4); // File size + bmpFile.write("\0\0\0\0", 4); // Reserved + int offsetToData = 54; + bmpFile.write(reinterpret_cast(&offsetToData), 4); // Offset to image data + + // DIB Header (BITMAPINFOHEADER) + int headerSize = 40; + bmpFile.write(reinterpret_cast(&headerSize), 4); // DIB header size + bmpFile.write(reinterpret_cast(&image.width), 4); // Image width + bmpFile.write(reinterpret_cast(&image.height), 4); // Image height + short planes = 1; + bmpFile.write(reinterpret_cast(&planes), 2); // Planes + short bitsPerPixel = 24; + bmpFile.write(reinterpret_cast(&bitsPerPixel), 2); // Bits per pixel + int compression = 0; + bmpFile.write(reinterpret_cast(&compression), 4); // Compression method + bmpFile.write(reinterpret_cast(&imageSize), 4); // Image size + int resolutionX = 2835; // Pixels per meter (72 DPI) + int resolutionY = 2835; // Pixels per meter (72 DPI) + bmpFile.write(reinterpret_cast(&resolutionX), 4); // Horizontal resolution + bmpFile.write(reinterpret_cast(&resolutionY), 4); // Vertical resolution + int colorsUsed = 0; + bmpFile.write(reinterpret_cast(&colorsUsed), 4); // Colors in color table + int importantColors = 0; + bmpFile.write(reinterpret_cast(&importantColors), 4); // Important color count + + // Write image data (bottom to top) + for (int y = image.height - 1; y >= 0; y--) { + for (int x = 0; x < image.width; x++) { + int index = (y * image.width + x) * 3 + padding * y; + bmpFile.write(reinterpret_cast(&image.data[index]), 1); // Blue channel + bmpFile.write(reinterpret_cast(&image.data[index + 1]), 1); // Green channel + bmpFile.write(reinterpret_cast(&image.data[index + 2]), 1); // Red channel + } + // Write padding bytes + for (int p = 0; p < padding; p++) { + bmpFile.write("\0", 1); + } + } + + bmpFile.close(); +} + void Image::copy(Image img) { if (width != img.width || height != img.height || ROI != img.ROI || channels != img.channels) @@ -196,7 +308,8 @@ uint8_t& Image::getPixelCH(int i, int j, int c) void Image::setAllPixel(int i, int j, const vector& pixel) { - if (pixel.size() < 0 || pixel.size() > 3 || i < 0 || i >= height || j < 0 || j >= width) throw std::out_of_range("Index out of range"); + if (pixel.size() < 0 || pixel.size() > 3 || i < 0 || i >= height || j < 0 || j >= width) + throw std::out_of_range("Index out of range"); for (int c = 0; c < channels; c++) { setPixelCH(i, j, c, pixel.at(c)); @@ -209,4 +322,4 @@ void Image::setPixelCH(int i, int j, int c, int value) if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); if (*refCount > 1) {}//function that nechami did data[(i * width * channels) + (j * channels) + c] = value; -} \ No newline at end of file +} From 5578e3826d3a31217562c7a000c1c5fe9a9f406b Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Tue, 30 Jul 2024 14:56:15 +0300 Subject: [PATCH 05/17] Removed functions.cpp --- functions.cpp | 114 -------------------------------------------------- functions.h | 11 ----- 2 files changed, 125 deletions(-) delete mode 100644 functions.cpp delete mode 100644 functions.h diff --git a/functions.cpp b/functions.cpp deleted file mode 100644 index 51f870c..0000000 --- a/functions.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "functions.h" -using namespace std; -struct BMP { - int width; - int height; - vector pixels; -}; - -Image readImage(string filename) { - BMP image; - - ifstream file(filename, ios::binary); - if (!file.is_open()) { - throw runtime_error("Unable to open file"); - } - - file.seekg(18, ios::beg); - file.read(reinterpret_cast(&image.width), sizeof(image.width)); - file.read(reinterpret_cast(&image.height), sizeof(image.height)); - - file.seekg(54, ios::beg); // Skip to pixel data - - int rowSize = ((image.width * 3 + 3) / 4) * 4; // Calculate padded row size - image.pixels.resize(rowSize * image.height); - - for (int i = 0; i < image.height; i++) { - file.read(reinterpret_cast(&image.pixels[(image.height - 1 - i) * rowSize]), rowSize); - } - - file.close(); - - Image result(image.width, image.height, 3, image.pixels); - int p; - return result; -} - -void writeImage(const std::string& filename, const Image& image) { - std::ofstream bmpFile(filename, std::ios::out | std::ios::binary); - if (!bmpFile) { - std::cerr << "Error opening file for writing." << std::endl; - return; - } - - int rowSize = image.width * 3; // Row size including padding - int padding = (4 - (rowSize % 4)) % 4; - int rowSizeWithPadding = rowSize + padding; - int fileSize = 54 + rowSizeWithPadding * image.height; // Total file size - int imageSize = rowSizeWithPadding * image.height; - - - bmpFile.put('B').put('M'); // BMP magic number - bmpFile.write(reinterpret_cast(&fileSize), 4); // File size - bmpFile.write("\0\0\0\0", 4); // Reserved - int offsetToData = 54; - bmpFile.write(reinterpret_cast(&offsetToData), 4); // Offset to image data - - // DIB Header (BITMAPINFOHEADER) - int headerSize = 40; - bmpFile.write(reinterpret_cast(&headerSize), 4); // DIB header size - bmpFile.write(reinterpret_cast(&image.width), 4); // Image width - bmpFile.write(reinterpret_cast(&image.height), 4); // Image height - short planes = 1; - bmpFile.write(reinterpret_cast(&planes), 2); // Planes - short bitsPerPixel = 24; - bmpFile.write(reinterpret_cast(&bitsPerPixel), 2); // Bits per pixel - int compression = 0; - bmpFile.write(reinterpret_cast(&compression), 4); // Compression method - bmpFile.write(reinterpret_cast(&imageSize), 4); // Image size - int resolutionX = 2835; // Pixels per meter (72 DPI) - int resolutionY = 2835; // Pixels per meter (72 DPI) - bmpFile.write(reinterpret_cast(&resolutionX), 4); // Horizontal resolution - bmpFile.write(reinterpret_cast(&resolutionY), 4); // Vertical resolution - int colorsUsed = 0; - bmpFile.write(reinterpret_cast(&colorsUsed), 4); // Colors in color table - int importantColors = 0; - bmpFile.write(reinterpret_cast(&importantColors), 4); // Important color count - - // Write image data (bottom to top) - for (int y = image.height-1; y >=0; y--) { - for (int x = 0; x < image.width; x++) { - int index = (y * image.width + x) * 3+padding*y; - bmpFile.write(reinterpret_cast(&image.data[index]), 1); // Blue channel - bmpFile.write(reinterpret_cast(&image.data[index + 1]), 1); // Green channel - bmpFile.write(reinterpret_cast(&image.data[index+2]), 1); // Red channel - } - // Write padding bytes - for (int p = 0; p < padding; p++) { - bmpFile.write("\0", 1); - } - } - - bmpFile.close(); -} - -void DisplayImage(const Image& img) { - BITMAPINFO bmi = { 0 }; - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = img.width; - bmi.bmiHeader.biHeight = -img.height; // Minus height to display image right-side up - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 24; // Assuming 24-bit RGB image - bmi.bmiHeader.biCompression = BI_RGB; - //HWND console = GetConsoleWindow(); - //PAINTSTRUCT ps; - //HDC hdc = GetDC(console); - - HDC hdc = GetDC(NULL); - StretchDIBits(hdc, 0, 0, img.width, img.height, 0, 0, img.width, img.height, img.data, &bmi, DIB_RGB_COLORS, SRCCOPY); - //Sleep(30000); // Sleep for 30,000 milliseconds (30 seconds) - - ReleaseDC(NULL, hdc); - - //EndPaint(console, &ps); -} diff --git a/functions.h b/functions.h deleted file mode 100644 index 0f9670c..0000000 --- a/functions.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "Image.h" -#include -#include -#include -#include -Image readImage(std::string filename); - -void DisplayImage(const Image& img); - -void writeImage(const std::string& filename, const Image& image); \ No newline at end of file From c294632d2627f1657321bd62aa4aaae22c3d0530 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Wed, 31 Jul 2024 11:08:19 +0300 Subject: [PATCH 06/17] Update Image.h add resize, total, row, col --- Image.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Image.h b/Image.h index b158726..2c6b011 100644 --- a/Image.h +++ b/Image.h @@ -25,7 +25,7 @@ class Image } //c'tor - Image(int w, int h, int c, std::vector const& _data = {}) + Image(int w, int h, int c=3, std::vector const& _data = {}) { if (c != 1 && c != 3) throw std::exception("The channels must be 1 or 3"); @@ -37,7 +37,7 @@ class Image refCount = new int; *refCount = 1; ROI = width; - data = new uint8_t[(w * 2) * h * c]; + data = new uint8_t[(w + 2) * h * c]; if (!_data.empty()) memcpy(data, _data.data(), (width + 2) * height * channels); } @@ -104,15 +104,26 @@ class Image static void waitKey(int delay = 0); + static void resize(const Image& src, Image& dst, std::tuple const size); + + int total(); + std::tuple getSize(); std::vector getAllPixel(int i, int j) const; + uint8_t& getPixelCH(int i, int j, int c); + void setAllPixel(int i, int j, const std::vector& pixel); + void setPixelCH(int i, int j, int c, int value); + Image row(int y) const; + + Image col(int x) const; + bool empty(); Image clone()const; From 9fe1e38b9f1d40a9a3097a1408739a5b481628d0 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Wed, 31 Jul 2024 11:09:37 +0300 Subject: [PATCH 07/17] Update Image.cpp add implement of resize, row, col total --- Image.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/Image.cpp b/Image.cpp index fcbd6f2..3c6a22a 100644 --- a/Image.cpp +++ b/Image.cpp @@ -1,6 +1,26 @@ #include "Image.h" using namespace std; +Image Image::row(int y) const +{ + Image img(width, 1, channels); + for (int j = 0; j < width - 1; j++) + { + img.setAllPixel(0, j, getAllPixel(j, y)); + } + return img; +} + +Image Image::col(int x) const +{ + Image img(1, x, channels); + for (int i = 0; i < height - 1; i++) + { + img.setAllPixel(i, 0, getAllPixel(x, i)); + } + return img; +} + tuple Image::getSize() { return make_tuple(width, height); @@ -260,6 +280,7 @@ void Image::writeImage(const std::string& filename, const Image& image) bmpFile.close(); } + void Image::copy(Image img) { if (width != img.width || height != img.height || ROI != img.ROI || channels != img.channels) @@ -284,6 +305,60 @@ void Image::waitKey(int delay) this_thread::sleep_for(std::chrono::seconds(delay)); } +void Image::resize(const Image& src, Image& dst, tuple const size) +{ + Image image = src; + + int oldWidth = src.width; + int oldHeight = src.height; + int newWidth = get<0>(size); + int newHeight = get<1>(size); + Image resImage(newWidth, newHeight); + + for (int y = 0; y < newHeight; ++y) { + for (int x = 0; x < newWidth; ++x) { + float gx = ((float)(x + 0.5) / newWidth) * oldWidth - 0.5; + float gy = ((float)(y + 0.5) / newHeight) * oldHeight - 0.5; + + int gxi = static_cast(gx); + int gyi = static_cast(gy); + + float c00r = image.getPixelCH(gyi, gxi, 0); + float c00g = image.getPixelCH(gyi, gxi, 1); + float c00b = image.getPixelCH(gyi, gxi, 2); + + float c10r = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 0) : c00r; + float c10g = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 1) : c00g; + float c10b = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 2) : c00b; + + float c01r = (gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi, 0) : c00r; + float c01g = (gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi, 1) : c00g; + float c01b = (gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi, 2) : c00b; + + float c11r = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi + 1, 0) : c00r; + float c11g = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi + 1, 1) : c00g; + float c11b = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi + 1, 2) : c00b; + + float wx = gx - gxi; + float wy = gy - gyi; + + uint8_t r = (1 - wx) * (1 - wy) * c00r + wx * (1 - wy) * c10r + (1 - wx) * wy * c01r + wx * wy * c11r; + uint8_t g = (1 - wx) * (1 - wy) * c00g + wx * (1 - wy) * c10g + (1 - wx) * wy * c01g + wx * wy * c11g; + uint8_t b = (1 - wx) * (1 - wy) * c00b + wx * (1 - wy) * c10b + (1 - wx) * wy * c01b + wx * wy * c11b; + + vector pixel = { r, g, b }; + + resImage.setAllPixel(y, x, pixel); + } + } + dst = resImage; +} + +int Image::total() +{ + return width * height; +} + vector Image::getAllPixel(int i, int j) const { if (i < 0 || i >= height || j < 0 || j >= width) { From 62c3c40de84f63de573a3933ad125acf105a1077 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Mon, 5 Aug 2024 14:42:32 +0300 Subject: [PATCH 08/17] Update Image.cpp all of the code after the cr --- Image.cpp | 234 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 175 insertions(+), 59 deletions(-) diff --git a/Image.cpp b/Image.cpp index 3c6a22a..5f23ab9 100644 --- a/Image.cpp +++ b/Image.cpp @@ -1,8 +1,60 @@ #include "Image.h" using namespace std; + +Image::Image() +{ + refCount = new int; + data = nullptr; +} + +Image::Image(Image const& other, int tl_x, int tl_y, int ROI_width, int ROI_height) +{ + if (tl_x<0 || tl_y + ROI_width > other.ROI || tl_y<0 || tl_x + ROI_height>other.height) + throw std::invalid_argument("Bad view image dimension"); + width = ROI_width; + height = ROI_height; + ROI = other.ROI; + channels = other.channels; + data = other.data + tl_x * ROI + tl_y; + refCount = (other.refCount); + (*refCount)++; +} + +Image::Image(Image const& other) + :width(other.width), height(other.height), ROI(other.ROI), channels(other.channels) +{ + data = other.data; + refCount = (other.refCount); + (*refCount)++; +} + +Image::Image(Image&& other)noexcept : + width(other.width), height(other.height), ROI(other.ROI), channels(other.channels) +{ + data = other.data; + other.data = nullptr; + refCount = (other.refCount); + other.refCount = nullptr; +} + +Image::~Image() +{ + if (refCount) + { + if (*refCount == 1) + { + delete refCount; + delete[] data; + } + else + (*refCount)--; + } +} + Image Image::row(int y) const { + if (y > height) throw exception("index out of range"); Image img(width, 1, channels); for (int j = 0; j < width - 1; j++) { @@ -13,7 +65,8 @@ Image Image::row(int y) const Image Image::col(int x) const { - Image img(1, x, channels); + if (x > width) throw exception("index out of range"); + Image img(1, height, channels); for (int i = 0; i < height - 1; i++) { img.setAllPixel(i, 0, getAllPixel(x, i)); @@ -50,30 +103,45 @@ vector> Image::splitChannels()const for (int index = 0; index < width * height * channels; index++) { if (index % 3 == 0) - red.push_back(data[index]); + blue.push_back(data[index]); else { if (index % 3 == 1) green.push_back(data[index]); else - blue.push_back(data[index]); + red.push_back(data[index]); } } vector>res({ red,green,blue }); return res; } +std::vector Image::mergeChannels(std::vector> v) +{ + if (v.size() != 3) + throw exception("the input is invalid"); + if (v[0].size() != v[1].size() || v[0].size() != v[2].size()) + throw exception("the input is invalid"); + vectorres; + for (int i = 0; i < v[0].size(); i++) + { + res.push_back(v[0][i]); + res.push_back(v[1][i]); + res.push_back(v[2][i]); + } + return res; +} + vector Image::getData() { vectordata; - for (int i = 0; i < width * height * channels; i++) + for (int i = 0; i < width * channels * height; i++) { data.push_back(this->data[i]); } return data; } - std::vector Image::at(int i, int j) { if (i < 0 || i >= height || j < 0 || j >= width) { throw std::out_of_range("Index out of range"); @@ -119,6 +187,47 @@ Image Image::operator+(uint8_t value) const return m; } +Image Image::operator+(Image const& other) const +{ + if (width == other.width) + { + int c = other.channels == 3 || channels == 3 ? 3 : 1; + Image res(width, height + other.height, c); + int index = 0; + for (int i = 0; i < width * height * channels; i++) + { + res.data[index++] = data[i]; + } + for (int i = 0; i < other.width * other.height * other.channels; i++) + { + res.data[index++] = other.data[i]; + } + return res; + } + if (height == other.height) + { + int c = other.channels == 3 || channels == 3 ? 3 : 1; + Image res(width + other.width, height, c); + int index = 0; + int i1 = 0; + int i2 = 0; + for (int j = 0; j < height; j++) + { + for (int i = 0; i < width; i++) + { + res.data[index++] = data[i1++]; + } + for (int i = 0; i < other.width; i++) + { + res.data[index++] = data[i2++]; + } + } + return res; + } + else + return Image(); +} + Image Image::operator-(uint8_t value) const { Image m(width, height, channels); @@ -130,7 +239,7 @@ Image Image::operator-(uint8_t value) const bool Image::operator==(Image const& other) const { if (width != other.width || height != other.height || channels != other.channels) - throw exception("It is impossible to compare objects with different dimensions"); + return false; for (int i = 0; i < width * height * channels; i++) { if (other.data[i] != data[i]) @@ -162,17 +271,12 @@ Image Image::operator=(Image const& other) return m; } -uint8_t& Image::operator()(int i, int j, int c) { - if (i < 0 || i >= height || j < 0 || j >= width || c < 0 || c >= channels) { - throw std::out_of_range("Index out of range"); - } - return data[(i * width * channels) + (j * channels) + c]; -} + Image Image::readImage(string filename) { - int width, height; + int width = 0, height = 0; vector pixels; ifstream file(filename, ios::binary); @@ -187,10 +291,11 @@ Image Image::readImage(string filename) file.seekg(54, ios::beg); // Skip to pixel data int rowSize = ((width * 3 + 3) / 4) * 4; // Calculate padded row size - pixels.resize(rowSize * height); - - for (int i = 0; i < height; i++) { - file.read(reinterpret_cast(&pixels[(height - 1 - i) * rowSize]), rowSize); + int padding = rowSize - width * 3; + pixels.resize(width * 3 * height); + for (int i = height - 1; i >= 0; i--) { + file.read(reinterpret_cast(&pixels[i * width * 3]), width * 3); + file.seekg(padding, ios::cur); } file.close(); @@ -199,8 +304,30 @@ Image Image::readImage(string filename) return result; } -void Image::DisplayImage(const Image& img) +void Image::displayImage(const Image& img, int delay) { + int rowSize = img.width * 3; // Calculate padded row size + int padding = (((img.width * 3 + 3) / 4) * 4) - img.width * 3; + int rowSizeWithPadding = ((img.width * 3 + 3) / 4) * 4; + vectordata; + uint8_t* data2 = new uint8_t[rowSizeWithPadding * img.height]; + int i = 0; + for (int y = 0; y < img.height; y++) { + for (int x = 0; x < img.width; x++) { + int index = (y * img.width + x) * 3; + data2[i++] = img.data[index]; // Blue channel + data2[i++] = img.data[index + 1]; // Blue channel + data2[i++] = img.data[index + 2]; // Blue channel + + } + // Write padding bytes + for (int p = 0; p < padding; p++) { + data2[i++] = uint8_t("\0"); + } + } + + + BITMAPINFO bmi = { 0 }; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = img.width; @@ -208,17 +335,16 @@ void Image::DisplayImage(const Image& img) bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; // Assuming 24-bit RGB image bmi.bmiHeader.biCompression = BI_RGB; - //HWND console = GetConsoleWindow(); - //PAINTSTRUCT ps; - //HDC hdc = GetDC(console); - HDC hdc = GetDC(NULL); - StretchDIBits(hdc, 0, 0, img.width, img.height, 0, 0, img.width, img.height, img.data, &bmi, DIB_RGB_COLORS, SRCCOPY); - //Sleep(30000); // Sleep for 30,000 milliseconds (30 seconds) + HWND hwnd = CreateWindowA("STATIC", "Image Viewer", WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, img.width, img.height, NULL, NULL, GetModuleHandle(NULL), NULL); + + HDC hdc = GetDC(hwnd); + StretchDIBits(hdc, 0, 0, img.width, img.height, 0, 0, img.width, img.height, data2, &bmi, DIB_RGB_COLORS, SRCCOPY); - ReleaseDC(NULL, hdc); + img.waitKey(delay); + ReleaseDC(hwnd, hdc); + DestroyWindow(hwnd); - //EndPaint(console, &ps); } void Image::writeImage(const std::string& filename, const Image& image) @@ -229,9 +355,10 @@ void Image::writeImage(const std::string& filename, const Image& image) return; } - int rowSize = image.width * 3; // Row size including padding - int padding = (4 - (rowSize % 4)) % 4; - int rowSizeWithPadding = rowSize + padding; + int rowSize = image.width * 3; // Calculate padded row size + // Row size including padding + int padding = (((image.width * 3 + 3) / 4) * 4) - image.width * 3; + int rowSizeWithPadding = ((image.width * 3 + 3) / 4) * 4; int fileSize = 54 + rowSizeWithPadding * image.height; // Total file size int imageSize = rowSizeWithPadding * image.height; @@ -266,7 +393,7 @@ void Image::writeImage(const std::string& filename, const Image& image) // Write image data (bottom to top) for (int y = image.height - 1; y >= 0; y--) { for (int x = 0; x < image.width; x++) { - int index = (y * image.width + x) * 3 + padding * y; + int index = (y * image.width + x) * 3; bmpFile.write(reinterpret_cast(&image.data[index]), 1); // Blue channel bmpFile.write(reinterpret_cast(&image.data[index + 1]), 1); // Green channel bmpFile.write(reinterpret_cast(&image.data[index + 2]), 1); // Red channel @@ -281,52 +408,38 @@ void Image::writeImage(const std::string& filename, const Image& image) } -void Image::copy(Image img) -{ - if (width != img.width || height != img.height || ROI != img.ROI || channels != img.channels) - throw out_of_range("Index out of range"); - data = img.data; - (*img.refCount)++; - (*refCount)--; -} - -void Image::copy2(Image img) -{ - if (width != img.width || height != img.height || ROI != img.ROI || channels != img.channels) - throw out_of_range("Index out of range"); - for (int i = 0; i < width * height * channels; i++) - { - *(data + i) = *(img.data + i); - } -} - void Image::waitKey(int delay) { this_thread::sleep_for(std::chrono::seconds(delay)); } -void Image::resize(const Image& src, Image& dst, tuple const size) +Image Image::resize(const Image& src, tuple const size) { Image image = src; - int oldWidth = src.width; int oldHeight = src.height; int newWidth = get<0>(size); int newHeight = get<1>(size); Image resImage(newWidth, newHeight); - + //double loop of through each pixel in the new image (newWidth and newHeight). for (int y = 0; y < newHeight; ++y) { - for (int x = 0; x < newWidth; ++x) { + for (int x = 0; x < newWidth; ++x) + { + //the positions in the original image that correspond to a pixel in the new image. float gx = ((float)(x + 0.5) / newWidth) * oldWidth - 0.5; float gy = ((float)(y + 0.5) / newHeight) * oldHeight - 0.5; - + + //The integer index of the pixel position in the original image. int gxi = static_cast(gx); int gyi = static_cast(gy); + //The pixel values ​​(red, green, blue) at the point (gxi, gyi) in the original image float c00r = image.getPixelCH(gyi, gxi, 0); float c00g = image.getPixelCH(gyi, gxi, 1); float c00b = image.getPixelCH(gyi, gxi, 2); + //The values ​​of the pixels adjacent to the point (gxi, gyi) in the original image. + // If the pixels go out of bounds, we use the existing pixel values. float c10r = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 0) : c00r; float c10g = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 1) : c00g; float c10b = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 2) : c00b; @@ -339,19 +452,22 @@ void Image::resize(const Image& src, Image& dst, tuple const size) float c11g = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi + 1, 1) : c00g; float c11b = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi + 1, 2) : c00b; + //Calculation of weights for interpolation. Representing the relative distance of the new pixel from the original whole pixel. float wx = gx - gxi; float wy = gy - gyi; + //Calculating the new pixel values ​​using interpolation uint8_t r = (1 - wx) * (1 - wy) * c00r + wx * (1 - wy) * c10r + (1 - wx) * wy * c01r + wx * wy * c11r; uint8_t g = (1 - wx) * (1 - wy) * c00g + wx * (1 - wy) * c10g + (1 - wx) * wy * c01g + wx * wy * c11g; uint8_t b = (1 - wx) * (1 - wy) * c00b + wx * (1 - wy) * c10b + (1 - wx) * wy * c01b + wx * wy * c11b; - vector pixel = { r, g, b }; + //Update the pixel in the new image + vector pixel = { r, g, b }; resImage.setAllPixel(y, x, pixel); } } - dst = resImage; + return resImage; } int Image::total() @@ -361,7 +477,7 @@ int Image::total() vector Image::getAllPixel(int i, int j) const { - if (i < 0 || i >= height || j < 0 || j >= width) { + if (i < 0 || i >= width || j < 0 || j >= height) { throw std::out_of_range("Index out of range"); } @@ -383,7 +499,7 @@ uint8_t& Image::getPixelCH(int i, int j, int c) void Image::setAllPixel(int i, int j, const vector& pixel) { - if (pixel.size() < 0 || pixel.size() > 3 || i < 0 || i >= height || j < 0 || j >= width) + if (pixel.size() < 0 || pixel.size() > 3 || i < 0 || i >= height || j < 0 || j >= width) throw std::out_of_range("Index out of range"); for (int c = 0; c < channels; c++) { @@ -391,10 +507,10 @@ void Image::setAllPixel(int i, int j, const vector& pixel) } } +//not return anything, here the copy on write, so what i need to return? void Image::setPixelCH(int i, int j, int c, int value) { if (value < 0 || value>255) throw out_of_range("Index out of range"); if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); - if (*refCount > 1) {}//function that nechami did data[(i * width * channels) + (j * channels) + c] = value; } From e123fa31da3331ba32e9e6a77081d0924371ad45 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Mon, 5 Aug 2024 14:43:13 +0300 Subject: [PATCH 09/17] Update Image.h all of the code after the cr --- Image.h | 121 +++++++++++++++++++++++--------------------------------- 1 file changed, 50 insertions(+), 71 deletions(-) diff --git a/Image.h b/Image.h index 2c6b011..b8663af 100644 --- a/Image.h +++ b/Image.h @@ -6,26 +6,22 @@ #include #include #include - +#include class Image { public: int width; int height; + int channels; int ROI; int* refCount; - int channels; uint8_t* data; //defult c'tor - Image() - { - refCount = new int; - data = nullptr; - } - + Image(); //c'tor - Image(int w, int h, int c=3, std::vector const& _data = {}) + //Image(int w, int h, int c = 3, std::vector const& _data = {}); + Image(int w, int h, int c = 3, std::vector const& _data = {}) { if (c != 1 && c != 3) throw std::exception("The channels must be 1 or 3"); @@ -37,119 +33,102 @@ class Image refCount = new int; *refCount = 1; ROI = width; - data = new uint8_t[(w + 2) * h * c]; + data = new uint8_t[width * c * height]; if (!_data.empty()) - memcpy(data, _data.data(), (width + 2) * height * channels); + memcpy(data, _data.data(), width * c * height); } //view c'tor - Image(Image const& other, int tl_x, int tl_y, int ROI_width, int ROI_height) - { - if (tl_x<0 || tl_y + ROI_width > other.ROI || tl_y<0 || tl_x + ROI_height>other.height) - throw std::invalid_argument("Bad view image dimension"); - width = ROI_width; - height = ROI_height; - ROI = other.ROI; - channels = other.channels; - data = other.data + tl_x * ROI + tl_y; - refCount = (other.refCount); - (*refCount)++; - } + Image(Image const& other, int tl_x, int tl_y, int ROI_width, int ROI_height); //copy c'tor - Image(Image const& other) - :width(other.width), height(other.height), ROI(other.ROI), channels(other.channels) - { - data = other.data; - refCount = (other.refCount); - (*refCount)++; - } + Image(Image const& other); //move c'tor - Image(Image&& other)noexcept : - width(other.width), height(other.height), ROI(other.ROI), channels(other.channels) - { - data = other.data; - other.data = nullptr; - refCount = (other.refCount); - other.refCount = nullptr; - } - + Image(Image&& other)noexcept; + //d'tor - ~Image() - { - if (refCount) - { - if (*refCount == 1) - { - delete refCount; - delete[] data; - } - else - (*refCount)--; - } - } - + ~Image(); + //get a path of image - read the image and return it static Image readImage(std::string filename); - static void DisplayImage(const Image& img); - + //get an image and show it in the screem for 5 minutes by defualt + static void displayImage(const Image& img, int delay=5); + + //get a path and an image and write the image in the path static void writeImage(const std::string& filename, const Image& image); + //stop the program for 5 minutes by defualt + static void waitKey(int delay = 5); + + //get a sorce image and new size- and return a new image in the new size + static Image resize(const Image& src, std::tuple const size); - void copy/*AndWrite*/(Image img); - - void copy2/*AndWrite*/(Image img); - - static void waitKey(int delay = 0); - - static void resize(const Image& src, Image& dst, std::tuple const size); - + //return the whole size of the image- width*height int total(); + //return a tuple of the width and height std::tuple getSize(); - + //get a row and a col and return the pixel in this place std::vector getAllPixel(int i, int j) const; + //get a row, a col and number-0/1/2 and return the specific channel in a pixel 0-B, 1-G, 2-R uint8_t& getPixelCH(int i, int j, int c); + //get a row, a col and a vector of three number- B,G,R- and set in this place this vector of pixel void setAllPixel(int i, int j, const std::vector& pixel); + //get a row, a col and number-0/1/2 and value - and set to specific channel in a pixel- 0-B, 1-G, 2-R the value void setPixelCH(int i, int j, int c, int value); - + //get a number of row and return a new Image of width*1 with the data of this row Image row(int y) const; - + + //get a number of col and return a new Image of 1*height with the data of this col Image col(int x) const; + //return true if the image is empty bool empty(); + //copy the data of the Image Image clone()const; + //split the data of the image to three vector of B,G,R std::vector>splitChannels()const; + //get three vectors - B,G,R and marge them together + std::vector mergeChannels(std::vector>v); + + //return the data of the image std::vectorgetData(); + //returns the location of the row and column and the color sent uint8_t at(int i, int j, int c); + //returns the location of the row and column sent std::vector at(int i, int j); - + //pointer to start of a raw uint8_t* ptr(int i); + //pointer to start of a pixel uint8_t* ptr(int i, int j); + //pointer to specific channel in a pixel uint8_t* ptr(int i, int j, int c); - + //like copy c'tor Image operator=(Image const& other); - uint8_t& operator()(int i, int j, int c); - - Image operator +(uint8_t value) const; // by element + //add to all the pixels the value + Image operator +(uint8_t value) const; + + //connect tow Image if match + Image operator +(Image const& other) const; + //sub to all the pixels the value Image operator -(uint8_t value) const; // by element bool operator ==(Image const& other) const; // compare by element to other image From 3d822e1c7f45bad2d3c3cee33cb8590787e86d2a Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Mon, 5 Aug 2024 14:44:54 +0300 Subject: [PATCH 10/17] Add files via upload add the .natvis file to the image watch --- Image.natvis | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Image.natvis diff --git a/Image.natvis b/Image.natvis new file mode 100644 index 0000000..0150a3a --- /dev/null +++ b/Image.natvis @@ -0,0 +1,26 @@ + + + + + + + + + + + + + UINT8 + + + 3 + + width + height + data + width*3 + + + + \ No newline at end of file From 5e3073f20e9cee61b773e10eb58965566422e0ae Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Wed, 7 Aug 2024 13:09:52 +0300 Subject: [PATCH 11/17] Update Image.cpp delete the Incorrect line in at function --- Image.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Image.cpp b/Image.cpp index 5f23ab9..42f5904 100644 --- a/Image.cpp +++ b/Image.cpp @@ -151,7 +151,6 @@ std::vector Image::at(int i, int j) { for (int c = 0; c < channels; ++c) { pixel[c] = data[(i * width * channels) + (j * channels) + c]; } - pixel[0] = 180; return pixel; } From fcca209cbf9ffae1d6a88423196f49ea4109364e Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Mon, 12 Aug 2024 10:48:37 +0300 Subject: [PATCH 12/17] Update Image.cpp the changes after the tests --- Image.cpp | 297 ++++++++++++++++++++++++++---------------------------- 1 file changed, 145 insertions(+), 152 deletions(-) diff --git a/Image.cpp b/Image.cpp index 42f5904..37b899c 100644 --- a/Image.cpp +++ b/Image.cpp @@ -1,11 +1,28 @@ #include "Image.h" using namespace std; - -Image::Image() +Image::Image(int w, int h, int c, std::vector const& _data) { + if (c != 1 && c != 3) + throw std::exception("The channels must be 1 or 3"); + if (w < 1 || h < 1) + throw std::exception("the arguments are invalid"); + width = w; + height = h; + channels = c; refCount = new int; - data = nullptr; + *refCount = 1; + ROI = width; + data = new uint8_t[width * 3 * height]; + if (!_data.empty()) + memcpy(data, _data.data(), width * 3 * height); + else + { + for (int i = 0; i < width * 3 * height; i++) + { + data[i] = 0; + } + } } Image::Image(Image const& other, int tl_x, int tl_y, int ROI_width, int ROI_height) @@ -29,15 +46,6 @@ Image::Image(Image const& other) (*refCount)++; } -Image::Image(Image&& other)noexcept : - width(other.width), height(other.height), ROI(other.ROI), channels(other.channels) -{ - data = other.data; - other.data = nullptr; - refCount = (other.refCount); - other.refCount = nullptr; -} - Image::~Image() { if (refCount) @@ -52,28 +60,69 @@ Image::~Image() } } + +std::vector Image::at(int i, int j)const { + if (i < 0 || i >= height || j < 0 || j >= width) { + throw std::out_of_range("Index out of range"); + } + + std::vector pixel(channels); + for (int c = 0; c < channels; ++c) { + pixel[c] = data[(i * width * channels) + (j * channels) + c]; + } + return pixel; +} + +uint8_t Image::at(int i, int j, int c) { + if (i < 0 || i >= height || j < 0 || j >= width || c < 0 || c>2) + throw out_of_range("Index out of range"); + return data[(i * width * channels) + (j * channels) + c]; +} + +uint8_t* Image::ptr(int i) +{ + if (i<0 || i>height) throw out_of_range("Index out of range"); + return data + i * width * channels; +} + +uint8_t* Image::ptr(int i, int j) +{ + if (i<0 || i>height || j<0 || j>width) throw out_of_range("Index out of range"); + return data + i * width * channels + j * channels; +} + +uint8_t* Image::ptr(int i, int j, int c) +{ + if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); + return data + i * width * channels + j * channels + c; +} + + Image Image::row(int y) const { - if (y > height) throw exception("index out of range"); + if (y > height-1) + throw invalid_argument("index out of range"); Image img(width, 1, channels); - for (int j = 0; j < width - 1; j++) + for (int j = 0; j < width; j++) { - img.setAllPixel(0, j, getAllPixel(j, y)); + img.setAllPixel(0, j, at(y,j)); } return img; } Image Image::col(int x) const { - if (x > width) throw exception("index out of range"); + if (x > width-1) + throw invalid_argument("index out of range"); Image img(1, height, channels); - for (int i = 0; i < height - 1; i++) + for (int i = 0; i < height; i++) { - img.setAllPixel(i, 0, getAllPixel(x, i)); + img.setAllPixel(i, 0, at(i, x)); } return img; } + tuple Image::getSize() { return make_tuple(width, height); @@ -81,7 +130,16 @@ tuple Image::getSize() bool Image::empty() { - return width * height == 0 || data == nullptr; + bool res= width * height == 0 || data == nullptr; + if (res) + return res; + res = true; + for (int i = 0; i < width * 3 * height; i++) + { + if (data[i] != 0) + return false; + } + return true; } Image Image::clone()const @@ -103,27 +161,27 @@ vector> Image::splitChannels()const for (int index = 0; index < width * height * channels; index++) { if (index % 3 == 0) - blue.push_back(data[index]); + red.push_back(data[index]); else { if (index % 3 == 1) green.push_back(data[index]); else - red.push_back(data[index]); + blue.push_back(data[index]); } } vector>res({ red,green,blue }); return res; } -std::vector Image::mergeChannels(std::vector> v) +vector Image::margeChannels(vector>v) { if (v.size() != 3) throw exception("the input is invalid"); - if (v[0].size() != v[1].size() || v[0].size() != v[2].size()) + if(v[0].size()!=v[1].size()|| v[0].size() != v[2].size()) throw exception("the input is invalid"); vectorres; - for (int i = 0; i < v[0].size(); i++) + for (int i = 0; i < v[0].size();i++) { res.push_back(v[0][i]); res.push_back(v[1][i]); @@ -135,59 +193,18 @@ std::vector Image::mergeChannels(std::vector> v) vector Image::getData() { vectordata; - for (int i = 0; i < width * channels * height; i++) + for (int i = 0; i < width*channels * height; i++) { data.push_back(this->data[i]); } return data; } -std::vector Image::at(int i, int j) { - if (i < 0 || i >= height || j < 0 || j >= width) { - throw std::out_of_range("Index out of range"); - } - - std::vector pixel(channels); - for (int c = 0; c < channels; ++c) { - pixel[c] = data[(i * width * channels) + (j * channels) + c]; - } - return pixel; -} - -uint8_t Image::at(int i, int j, int c) { - if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); - return data[(i * width * channels) + (j * channels) + c]; -} - -uint8_t* Image::ptr(int i) -{ - if (i<0 || i>height) throw out_of_range("Index out of range"); - return data + i * width * channels; -} - -uint8_t* Image::ptr(int i, int j) -{ - if (i<0 || i>height || j<0 || j>width) throw out_of_range("Index out of range"); - return data + i * width * channels + j * channels; -} - -uint8_t* Image::ptr(int i, int j, int c) -{ - if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); - return data + i * width * channels + j * channels + c; -} - - -Image Image::operator+(uint8_t value) const -{ - Image m(width, height, channels); - for (int index = 0; index < width * height * channels; index++) - m.data[index] = data[index] + value; - return m; -} Image Image::operator+(Image const& other) const { + if (width != other.width && height != other.height) + throw invalid_argument("the argument is invalid"); if (width == other.width) { int c = other.channels == 3 || channels == 3 ? 3 : 1; @@ -203,7 +220,7 @@ Image Image::operator+(Image const& other) const } return res; } - if (height == other.height) + else { int c = other.channels == 3 || channels == 3 ? 3 : 1; Image res(width + other.width, height, c); @@ -212,33 +229,52 @@ Image Image::operator+(Image const& other) const int i2 = 0; for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) + for (int i = 0; i < width*3; i++) { res.data[index++] = data[i1++]; } - for (int i = 0; i < other.width; i++) + for (int i = 0; i < other.width*3; i++) { - res.data[index++] = data[i2++]; + res.data[index++] = other.data[i2++]; } } return res; } - else - return Image(); +} + +Image Image::operator+(uint8_t value) const +{ + Image m(width, height, channels); + for (int index = 0; index < width * height * channels; index++) + { + if (data[index] + value > 255) + { + throw invalid_argument("the argument is invalid!!"); + } + m.data[index] = data[index] + value; + } + return m; } Image Image::operator-(uint8_t value) const { Image m(width, height, channels); for (int index = 0; index < width * height * channels; index++) + { + if (data[index] - value<0) + { + throw invalid_argument("the argument is invalid!!"); + } m.data[index] = data[index] - value; + + } return m; } bool Image::operator==(Image const& other) const { if (width != other.width || height != other.height || channels != other.channels) - return false; + throw exception("It is impossible to compare objects with different dimensions"); for (int i = 0; i < width * height * channels; i++) { if (other.data[i] != data[i]) @@ -271,11 +307,9 @@ Image Image::operator=(Image const& other) } - - Image Image::readImage(string filename) { - int width = 0, height = 0; + int width=0, height=0; vector pixels; ifstream file(filename, ios::binary); @@ -292,18 +326,18 @@ Image Image::readImage(string filename) int rowSize = ((width * 3 + 3) / 4) * 4; // Calculate padded row size int padding = rowSize - width * 3; pixels.resize(width * 3 * height); - for (int i = height - 1; i >= 0; i--) { - file.read(reinterpret_cast(&pixels[i * width * 3]), width * 3); + for (int i = height-1; i >=0; i--) { + file.read(reinterpret_cast(&pixels[i* width*3]), width*3); file.seekg(padding, ios::cur); } - + file.close(); Image result(width, height, 3, pixels); return result; } -void Image::displayImage(const Image& img, int delay) +void Image::displayImage(const Image& img,int delay) { int rowSize = img.width * 3; // Calculate padded row size int padding = (((img.width * 3 + 3) / 4) * 4) - img.width * 3; @@ -311,22 +345,19 @@ void Image::displayImage(const Image& img, int delay) vectordata; uint8_t* data2 = new uint8_t[rowSizeWithPadding * img.height]; int i = 0; - for (int y = 0; y < img.height; y++) { + for (int y = 0; y = 0; y--) { for (int x = 0; x < image.width; x++) { - int index = (y * image.width + x) * 3; + int index = (y * image.width + x)*3 ; bmpFile.write(reinterpret_cast(&image.data[index]), 1); // Blue channel bmpFile.write(reinterpret_cast(&image.data[index + 1]), 1); // Green channel bmpFile.write(reinterpret_cast(&image.data[index + 2]), 1); // Red channel @@ -420,48 +451,29 @@ Image Image::resize(const Image& src, tuple const size) int newWidth = get<0>(size); int newHeight = get<1>(size); Image resImage(newWidth, newHeight); - //double loop of through each pixel in the new image (newWidth and newHeight). for (int y = 0; y < newHeight; ++y) { - for (int x = 0; x < newWidth; ++x) - { - //the positions in the original image that correspond to a pixel in the new image. + for (int x = 0; x < resImage.width; ++x) { float gx = ((float)(x + 0.5) / newWidth) * oldWidth - 0.5; float gy = ((float)(y + 0.5) / newHeight) * oldHeight - 0.5; - - //The integer index of the pixel position in the original image. int gxi = static_cast(gx); int gyi = static_cast(gy); - - //The pixel values ​​(red, green, blue) at the point (gxi, gyi) in the original image - float c00r = image.getPixelCH(gyi, gxi, 0); - float c00g = image.getPixelCH(gyi, gxi, 1); - float c00b = image.getPixelCH(gyi, gxi, 2); - - //The values ​​of the pixels adjacent to the point (gxi, gyi) in the original image. - // If the pixels go out of bounds, we use the existing pixel values. - float c10r = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 0) : c00r; - float c10g = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 1) : c00g; - float c10b = (gxi + 1 < oldWidth) ? image.getPixelCH(gyi, gxi + 1, 2) : c00b; - - float c01r = (gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi, 0) : c00r; - float c01g = (gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi, 1) : c00g; - float c01b = (gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi, 2) : c00b; - - float c11r = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi + 1, 0) : c00r; - float c11g = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi + 1, 1) : c00g; - float c11b = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.getPixelCH(gyi + 1, gxi + 1, 2) : c00b; - - //Calculation of weights for interpolation. Representing the relative distance of the new pixel from the original whole pixel. + float c00r = image.at(gyi, gxi, 0); + float c00g = image.at(gyi, gxi, 1); + float c00b = image.at(gyi, gxi, 2); + float c10r = (gxi + 1 < oldWidth) ? image.at(gyi, gxi + 1, 0) : c00r; + float c10g = (gxi + 1 < oldWidth) ? image.at(gyi, gxi + 1, 1) : c00g; + float c10b = (gxi + 1 < oldWidth) ? image.at(gyi, gxi + 1, 2) : c00b; + float c01r = (gyi + 1 < oldHeight) ? image.at(gyi + 1, gxi, 0) : c00r; + float c01g = (gyi + 1 < oldHeight) ? image.at(gyi + 1, gxi, 1) : c00g; + float c01b = (gyi + 1 < oldHeight) ? image.at(gyi + 1, gxi, 2) : c00b; + float c11r = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.at(gyi + 1, gxi + 1, 0) : c00r; + float c11g = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.at(gyi + 1, gxi + 1, 1) : c00g; + float c11b = (gxi + 1 < oldWidth && gyi + 1 < oldHeight) ? image.at(gyi + 1, gxi + 1, 2) : c00b; float wx = gx - gxi; float wy = gy - gyi; - - //Calculating the new pixel values ​​using interpolation uint8_t r = (1 - wx) * (1 - wy) * c00r + wx * (1 - wy) * c10r + (1 - wx) * wy * c01r + wx * wy * c11r; uint8_t g = (1 - wx) * (1 - wy) * c00g + wx * (1 - wy) * c10g + (1 - wx) * wy * c01g + wx * wy * c11g; uint8_t b = (1 - wx) * (1 - wy) * c00b + wx * (1 - wy) * c10b + (1 - wx) * wy * c01b + wx * wy * c11b; - - - //Update the pixel in the new image vector pixel = { r, g, b }; resImage.setAllPixel(y, x, pixel); } @@ -471,45 +483,26 @@ Image Image::resize(const Image& src, tuple const size) int Image::total() { - return width * height; + return width * height*channels; } -vector Image::getAllPixel(int i, int j) const { - if (i < 0 || i >= width || j < 0 || j >= height) { - throw std::out_of_range("Index out of range"); - } - - std::vector pixel(channels); - for (int c = 0; c < channels; ++c) { - pixel[c] = data[(i * width * channels) + (j * channels) + c]; - } - return pixel; -} - -uint8_t& Image::getPixelCH(int i, int j, int c) -{ - if (i < 0 || i >= height || j < 0 || j >= width || c < 0 || c >= channels) { - throw std::out_of_range("Index out of range"); - } - return data[(i * width * channels) + (j * channels) + c]; -} - void Image::setAllPixel(int i, int j, const vector& pixel) { - if (pixel.size() < 0 || pixel.size() > 3 || i < 0 || i >= height || j < 0 || j >= width) - throw std::out_of_range("Index out of range"); + if (pixel.size() < 0 || pixel.size() > 3 || i < 0 || i >= height || j < 0 || j >= width) + throw std::invalid_argument("Index out of range"); for (int c = 0; c < channels; c++) { setPixelCH(i, j, c, pixel.at(c)); } } -//not return anything, here the copy on write, so what i need to return? void Image::setPixelCH(int i, int j, int c, int value) { - if (value < 0 || value>255) throw out_of_range("Index out of range"); - if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) throw out_of_range("Index out of range"); + if (value < 0 || value>255) + throw invalid_argument("Index out of range"); + if (i<0 || i>height || j<0 || j>width || c < 0 || c>2) + throw invalid_argument("Index out of range"); data[(i * width * channels) + (j * channels) + c] = value; } From f28cafcc1a331a43a49fa97448013a7cb448d4d0 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Mon, 12 Aug 2024 10:50:57 +0300 Subject: [PATCH 13/17] Update Image.h changes after the tests --- Image.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Image.h b/Image.h index b8663af..0c36978 100644 --- a/Image.h +++ b/Image.h @@ -71,11 +71,6 @@ class Image //return a tuple of the width and height std::tuple getSize(); - //get a row and a col and return the pixel in this place - std::vector getAllPixel(int i, int j) const; - - //get a row, a col and number-0/1/2 and return the specific channel in a pixel 0-B, 1-G, 2-R - uint8_t& getPixelCH(int i, int j, int c); //get a row, a col and a vector of three number- B,G,R- and set in this place this vector of pixel void setAllPixel(int i, int j, const std::vector& pixel); From 27a849d0dbc4a36b0e56bfd3ad6e3e055078a51a Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Mon, 12 Aug 2024 10:52:19 +0300 Subject: [PATCH 14/17] upload the tests --- test1.cpp | 539 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 test1.cpp diff --git a/test1.cpp b/test1.cpp new file mode 100644 index 0000000..0e545ae --- /dev/null +++ b/test1.cpp @@ -0,0 +1,539 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#ifdef _DEBUG +// the debug version that corresponds to the opencv_world420d.dll: +#pragma comment(lib, "opencv_world4100d.lib") +#else +// the release version that corresponds to the opencv_world420.dll: +#pragma comment(lib, "opencv_world4100.lib") +#endif + +#include "doctest.h" +#include "Image.h" +#include +#include +#define _CRTDBG_MAP_ALLOC //to get more details +#include +#include //for malloc and free + +using namespace std; +using namespace cv; + +TEST_CASE("check the memory management") +{ + _CrtMemState sOld; + _CrtMemState sNew; + _CrtMemState sDiff; + + _CrtMemCheckpoint(&sOld);//save the memory state + + { + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + auto data = img.getData(); + Image img2(img.width, img.height, 3, data); + Image img3(3, 3, 3); + Image img4 = img; + Image img5(img); + } + + _CrtMemCheckpoint(&sNew);//save the new memory state + + //if (_CrtMemDifference(&sDiff, &sOld, &sNew)) // if there is a difference + //{ + // OutputDebugString(L"-----------_CrtMemDumpStatistics ---------"); + // _CrtMemDumpStatistics(&sDiff); + // OutputDebugString(L"-----------_CrtMemDumpAllObjectsSince ---------"); + // _CrtMemDumpAllObjectsSince(&sOld); + // OutputDebugString(L"-----------_CrtDumpMemoryLeaks ---------"); + // _CrtDumpMemoryLeaks(); + //} + + CHECK(!_CrtMemDifference(&sDiff, &sOld, &sNew));// if there is a difference +} + +TEST_CASE("check the readImage function with invalid argumant") +{ + CHECK_THROWS_AS(Image::readImage("rest8.BMP"), runtime_error); +} + +TEST_CASE("check the readImage function") +{ + Mat m = imread("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK(img.width == m.cols); + CHECK(img.height == m.rows); + bool same = true; + for (int i = 0; i < img.width * img.height * img.channels; i++) + { + if (img.data[i] != m.data[i]) + same = false; + } + CHECK(same==true); +} + +TEST_CASE("check the readImage function with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK_THROWS_AS(Image::writeImage("C:\\Users\\The user\\Desktop\\aaaaaa\\rest8.BMP",img), runtime_error); +} + +TEST_CASE("check the writeImage function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image::writeImage("tryWriteImage.bmp", img); + Image img2 = Image::readImage("tryWriteImage.bmp"); + + CHECK(img.width == img2.width); + CHECK(img.height == img2.height); + bool same = true; + for (int i = 0; i < img.width * img.height * img.channels; i++) + { + if (img.data[i] != img2.data[i]) + same = false; + } + CHECK(same == true); +} + +//TEST_CASE("check the resize function") +//{ +// Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); +// Image img2 = Image::resize(img,make_tuple(200,300)); +// Mat img3 = imread("C:\\Users\\The user\\Desktop\\rest8.BMP"); +// Mat resizedImage; +// Size newSize(200,300); // Set the new size (width, height) +// resize(img3, resizedImage, newSize); +// +// bool same = true; +// +// for (int y = 0; y < resizedImage.rows; y++) +// { +// for (int x = 0; x < resizedImage.cols; x++) +// { +// Vec3b pixel = resizedImage.at(y, x); +// if (pixel[0] != img2.data[y * img2.width * 3 + x]) +// same = false; +// if (pixel[1] != img2.data[y * img2.width * 3 + x+1]) +// same = false; +// if (pixel[2] != img2.data[y * img2.width * 3 + x+2]) +// same = false; +// } +// } +// +// CHECK(same == true); +//} + +TEST_CASE("check the total function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK(img.total() == 201588); +} + +TEST_CASE("check the getSize function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK(img.getSize() == std::make_tuple(314, 214)); +} + +TEST_CASE("check the at function that return a specific channel with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK_THROWS_AS(img.at(215, 1, 1), std::out_of_range); + CHECK_THROWS_AS(img.at(1, 314, 1), std::out_of_range); + CHECK_THROWS_AS(img.at(1, 1, 3), std::out_of_range); +} + +TEST_CASE("check the at function that return a specific channel") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK(img.at(1, 1, 1) == 230); + CHECK(img.at(213, 313, 2) == 38); + CHECK(img.at(100,100,1) == 121); +} + +TEST_CASE("check the at function that return the BGR of the pixel with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK_THROWS_AS(img.at(215, 1), out_of_range); + CHECK_THROWS_AS(img.at(1, 314), out_of_range); +} + +TEST_CASE("check the at function that return the BGR of the pixel") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + vectorv{ 254,225,233 }; + vectorv2{ 42,36,38 }; + vectorv3{ 62,51,50 }; + auto res = img.at(0, 0); + auto res2 = img.at(213, 313); + auto res3 = img.at(103,145); + CHECK(res == v); + CHECK(res2 == v2); + CHECK(res3 == v3); +} + +TEST_CASE("check the operator== with other Image, with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2 = Image::resize(img, make_tuple(100, 100)); + Image img3 = Image::resize(img, make_tuple(214, 100)); + CHECK_THROWS_AS(img == img2, exception); + CHECK_THROWS_AS(img==img3,exception); +} + +TEST_CASE("check the operator== with other Image") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2= Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img3= Image::readImage("C:\\Users\\The user\\Desktop\\rest10.BMP"); + Image img4 = Image::resize(img3, make_tuple(img.width, img.height)); + + bool isSame = img == img2; + CHECK(isSame==true); + + isSame = img == img4; + CHECK(isSame != true); +} + +TEST_CASE("check the operator== with value") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + bool isSame=img == 3; + bool res = true; + + for (int i = 0; i < img.width * img.height * img.channels; i++) + { + if (img.data[i] != 3) + res = false; + } + CHECK(isSame == res); +} + +TEST_CASE("check the operator+ with value, with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK_THROWS_AS(img + 255, invalid_argument); +} + +TEST_CASE("check the operator+ with value") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + Image img2 = img + 0; + bool res = true; + + for (int i = 0; i < img.width * img.height * img.channels; i++) + { + if (img.data[i]+0 != img2.data[i]) + res = false; + } + CHECK(res == true); +} + +TEST_CASE("check the operator- with value with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + CHECK_THROWS_AS(img - 444, invalid_argument); +} + +TEST_CASE("check the operator- with value") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + Image img2 = img - 3; + bool res = true; + + for (int i = 0; i < img.width * img.height * img.channels; i++) + { + if (img.data[i] - 3 != img2.data[i]) + res = false; + } + CHECK(res == true); +} + +TEST_CASE("check the operator= with other image") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + int refCount = *(img.refCount); + Image img2 = img; + + CHECK(img.data == img2.data); + CHECK(img.width == img2.width); + CHECK(img.height == img2.height); + CHECK(img.ROI == img2.ROI); + CHECK(img.channels == img2.channels); + CHECK(img.refCount == img2.refCount); + CHECK(*(img.refCount) == refCount+1); +} + +TEST_CASE("check the operator+ with other image, with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2 = Image::readImage("C:\\Users\\The user\\Desktop\\rest10.BMP"); + CHECK_THROWS_AS(img + img2, invalid_argument); +} + +TEST_CASE("check the operator+ with other image in case they both have the same width") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2 = Image::readImage("C:\\Users\\The user\\Desktop\\rest10.BMP"); + Image img3 = Image::resize(img2, make_tuple(img.width, img2.height)); + Image resImage = img + img3; + bool res = true; + int imgSize = img.total(); + int resImgSize = resImage.total(); + for (int i = 0; i < resImgSize; i++) + { + if (i < imgSize) + { + if (resImage.data[i] != img.data[i]) + res = false; + } + else + { + if (resImage.data[i] != img3.data[i-imgSize]) + res = false; + } + + } + CHECK(res == true); +} + +TEST_CASE("check the operator+ with other image in case they both have the same height") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2 = Image::readImage("C:\\Users\\The user\\Desktop\\rest10.BMP"); + Image img3 = Image::resize(img2, make_tuple(img2.width, img.height)); + Image resImage = img + img3; + bool res = true; + int imgSize = img.total(); + int resImgSize = resImage.total(); + int imgRowSize = img.width * 3; + for (int i = 0; i < resImage.height; i++) + { + int toThisLine = i * resImage.width * 3; + for (int j = 0; j < img.width*3; j++) + { + if (resImage.data[toThisLine +j] != img.data[i*img.width*3+j]) + res = false; + } + for (int j = 0; j < img3.width * 3; j++) + { + if (resImage.data[toThisLine+img.width*3 + j] != img3.data[i * img3.width*3 + j]) + res = false; + } + } + CHECK(res == true); +} + +TEST_CASE("check the clone function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2 = img.clone(); + + CHECK(img.data != img2.data); + CHECK(img.width == img2.width); + CHECK(img.height == img2.height); + CHECK(img.ROI == img2.ROI); + CHECK(img.channels == img2.channels); + CHECK(img.refCount != img2.refCount); + bool res = true; + + for (int i = 0; i < img.width * img.height * img.channels; i++) + { + if (img.data[i] != img2.data[i]) + res = false; + } + CHECK(res == true); +} + +TEST_CASE("check the empty function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2(5, 5); + Image img3; + + CHECK(img.empty() == false); + CHECK(img2.empty() == true); + CHECK(img3.empty() == true); +} + +TEST_CASE("check the getData function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + auto v = img.getData(); + bool res = true; + for (int i = 0; i < v.size(); i++) + { + if (v[i] != img.data[i]) + res = false; + } + CHECK(res== true); +} + +TEST_CASE("check the splitChannels function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Mat img2 = imread("C:\\Users\\The user\\Desktop\\rest8.BMP"); + auto v = img.splitChannels(); + vector channels; + split(img2, channels); + bool res = true; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < v[0].size(); j++) + { + if (v[i][j] != channels[i].data[j]) + res = false; + } + } + CHECK(res == true); +} + +TEST_CASE("check the margeChannels function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + auto v = img.splitChannels(); + auto v2 = Image::margeChannels(v); + bool res = true; + for (int i = 0; i < v2.size(); i++) + { + if (v2[i] != img.data[i]) + res = false; + } + CHECK(res == true); +} + +TEST_CASE("check the setAllPixel function with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + vectorpixel = { 122,111,145 }; + + CHECK_THROWS_AS(img.setAllPixel(1, 314, pixel), invalid_argument); + CHECK_THROWS_AS(img.setAllPixel(214,1,pixel), invalid_argument); +} + +TEST_CASE("check the setAllPixel function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2 = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + vectorpixel = { 255,0,0 }; + + img.setAllPixel(1, 1, pixel); + img.setAllPixel(213, 313, pixel); + img.setAllPixel(92, 39, pixel); + + auto v = img.at(1, 1); + auto v2=img2.at(1, 1); + bool same = v == v2; + bool sameV = v==pixel; + CHECK(same != true); + CHECK(sameV==true); + + v = img.at(213, 313); + v2 = img2.at(213, 313); + same = v == v2; + sameV = v == pixel; + CHECK(same != true); + CHECK(sameV == true); + + v = img.at(92, 39); + v2 = img2.at(92, 39); + same = v == v2; + sameV = v == pixel; + CHECK(same != true); + CHECK(sameV == true); +} + +TEST_CASE("check the setPixelCH function with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + CHECK_THROWS_AS(img.setPixelCH(1, 314, 3,2), invalid_argument); + CHECK_THROWS_AS(img.setPixelCH(214, 1, -1, 3), invalid_argument); + CHECK_THROWS_AS(img.setPixelCH(214, 1, 1, -3), invalid_argument); + CHECK_THROWS_AS(img.setPixelCH(214, 500, 1, -3), invalid_argument); + CHECK_THROWS_AS(img.setPixelCH(-214, 1, 1,-3), invalid_argument); +} + +TEST_CASE("check the setPixelCH function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + Image img2 = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + img.setPixelCH(1, 1, 0, 0); + img.setPixelCH(1, 1, 1, 0); + img.setPixelCH(1, 1, 2, 255); + + img.setPixelCH(95, 95, 0, 0); + img.setPixelCH(95, 95, 1, 0); + img.setPixelCH(95, 95, 2, 255); + + auto v = img.at(1, 1,1); + auto v2 = img2.at(1, 1,1); + bool same = v == v2; + bool sameC = v ==0; + + CHECK(same != true); + CHECK(sameC == true); + + v = img.at(95, 95,2); + v2 = img2.at(95, 95,2); + same = v == v2; + sameC = v == 255; + CHECK(same != true); + CHECK(sameC == true); +} + +TEST_CASE("check the row function with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + CHECK_THROWS_AS(img.row(500), invalid_argument); +} + +TEST_CASE("check the row function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + Image img2 = img.row(100); + + bool same = true; + + for (int i = 0; i < img.width*3; i++) + { + if (img.data[img.width*3*100+i] != img2.data[i]) + same = false; + } + + CHECK(same == true); +} + +TEST_CASE("check the col function with invalid argumant") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + CHECK_THROWS_AS(img.col(314), invalid_argument); +} + +TEST_CASE("check the col function") +{ + Image img = Image::readImage("C:\\Users\\The user\\Desktop\\rest8.BMP"); + + Image img2 = img.col(100); + + bool same = true; + + for (int i = 0; i < img2.height; i++) + { + auto pixel = img.at(i, 100); + auto pixel2 = img2.at(i,0); + if (pixel == pixel2) + continue; + else + same = false; + } + + CHECK(same == true); +} + +//test on the ptr functions, resize waitKey \ No newline at end of file From 4f22cd9114d02a15392e4be3d889655d3829dc2a Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Thu, 5 Sep 2024 10:47:28 +0300 Subject: [PATCH 15/17] Update Image.h better --- Image.h | 87 +++++++++++++++++++++------------------------------------ 1 file changed, 32 insertions(+), 55 deletions(-) diff --git a/Image.h b/Image.h index 0c36978..3993af9 100644 --- a/Image.h +++ b/Image.h @@ -12,118 +12,95 @@ class Image public: int width; int height; - int channels; + int channels; int ROI; int* refCount; uint8_t* data; //defult c'tor - Image(); - //c'tor - //Image(int w, int h, int c = 3, std::vector const& _data = {}); - Image(int w, int h, int c = 3, std::vector const& _data = {}) + Image() { - if (c != 1 && c != 3) - throw std::exception("The channels must be 1 or 3"); - if (w < 1 || h < 1) - throw std::exception("the arguments are invalid"); - width = w; - height = h; - channels = c; refCount = new int; - *refCount = 1; - ROI = width; - data = new uint8_t[width * c * height]; - if (!_data.empty()) - memcpy(data, _data.data(), width * c * height); + data = nullptr; } + //c'tor + Image(int w, int h, int c = 3, std::vector const& _data = {}); + //view c'tor Image(Image const& other, int tl_x, int tl_y, int ROI_width, int ROI_height); - + //copy c'tor Image(Image const& other); //move c'tor - Image(Image&& other)noexcept; - + Image(Image&& other)noexcept : + width(other.width), height(other.height), ROI(other.ROI), channels(other.channels) + { + data = other.data; + other.data = nullptr; + refCount = (other.refCount); + other.refCount = nullptr; + } + //d'tor ~Image(); - //get a path of image - read the image and return it + static Image readImage(std::string filename); - //get an image and show it in the screem for 5 minutes by defualt - static void displayImage(const Image& img, int delay=5); - - //get a path and an image and write the image in the path + static void displayImage(const Image& img,int delay); + static void writeImage(const std::string& filename, const Image& image); - //stop the program for 5 minutes by defualt - static void waitKey(int delay = 5); - - //get a sorce image and new size- and return a new image in the new size + + static void waitKey(int delay = 0); + static Image resize(const Image& src, std::tuple const size); - //return the whole size of the image- width*height int total(); - //return a tuple of the width and height std::tuple getSize(); - //get a row, a col and a vector of three number- B,G,R- and set in this place this vector of pixel void setAllPixel(int i, int j, const std::vector& pixel); - //get a row, a col and number-0/1/2 and value - and set to specific channel in a pixel- 0-B, 1-G, 2-R the value void setPixelCH(int i, int j, int c, int value); - //get a number of row and return a new Image of width*1 with the data of this row + Image row(int y) const; - - //get a number of col and return a new Image of 1*height with the data of this col + Image col(int x) const; - //return true if the image is empty bool empty(); - //copy the data of the Image Image clone()const; - //split the data of the image to three vector of B,G,R std::vector>splitChannels()const; - //get three vectors - B,G,R and marge them together - std::vector mergeChannels(std::vector>v); + static std::vectormargeChannels(std::vector>v); - //return the data of the image std::vectorgetData(); - //returns the location of the row and column and the color sent + uint8_t at(int i, int j, int c); - //returns the location of the row and column sent - std::vector at(int i, int j); + std::vector at(int i, int j)const; + - //pointer to start of a raw uint8_t* ptr(int i); - //pointer to start of a pixel uint8_t* ptr(int i, int j); - //pointer to specific channel in a pixel uint8_t* ptr(int i, int j, int c); - //like copy c'tor + + Image operator+(Image const& other) const; + Image operator=(Image const& other); - //add to all the pixels the value - Image operator +(uint8_t value) const; - - //connect tow Image if match - Image operator +(Image const& other) const; + Image operator +(uint8_t value) const; // by element - //sub to all the pixels the value Image operator -(uint8_t value) const; // by element bool operator ==(Image const& other) const; // compare by element to other image From 33284d6ff2c04e02168c08a9583d286e9dcaefdc Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Thu, 5 Sep 2024 10:48:31 +0300 Subject: [PATCH 16/17] Update Image.cpp better From 130305dedb3afaf0576899552bb7ba749034d6a8 Mon Sep 17 00:00:00 2001 From: NechamaRos Date: Tue, 10 Sep 2024 14:35:30 +0300 Subject: [PATCH 17/17] Update Image.h Added a note on each function --- Image.h | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Image.h b/Image.h index 3993af9..6ad55b2 100644 --- a/Image.h +++ b/Image.h @@ -60,51 +60,72 @@ class Image int total(); + //return a tuple of the width and height std::tuple getSize(); + //get a row, a col and a vector of three number- B,G,R- and set in this place this vector of pixel void setAllPixel(int i, int j, const std::vector& pixel); + //get a row, a col and number-0/1/2 and value - and set to specific channel in a pixel- 0-B, 1-G, 2-R the value void setPixelCH(int i, int j, int c, int value); + //get a number of row and return a new Image of width*1 with the data of this row Image row(int y) const; + //get a number of col and return a new Image of 1*height with the data of this col Image col(int x) const; + //return true if the image is empty bool empty(); + //return new image by copy the data Image clone()const; + //split the data of the image to three vector of B,G,R std::vector>splitChannels()const; + //get three vectors - B,G,R and marge them together static std::vectormargeChannels(std::vector>v); + //return the data of the image std::vectorgetData(); + //returns the location of the row and column and the color sent uint8_t at(int i, int j, int c); + //returns the location of the row and column sent std::vector at(int i, int j)const; + //pointer to start of a raw uint8_t* ptr(int i); + //pointer to start of a pixel uint8_t* ptr(int i, int j); + //pointer to specific channel in a pixel uint8_t* ptr(int i, int j, int c); - - Image operator+(Image const& other) const; - + + //like copy c'tor Image operator=(Image const& other); + //add to all the pixels the value Image operator +(uint8_t value) const; // by element + //connect tow Image if match + Image operator+(Image const& other) const; + + //sub to all the pixels the value Image operator -(uint8_t value) const; // by element + // compare by element to other image bool operator ==(Image const& other) const; // compare by element to other image + // compare all elements to value bool operator ==(uint8_t const& value) const; // compare all elements to value };