Skip to content

Commit

Permalink
[DIP] Add BMP image encoding and decoding. (#186)
Browse files Browse the repository at this point in the history
* [DIP] Complete the constructor declaration of the image container Img class

* [DIP] Adds definitions for constructor and member functions of the Img class

* [DIP] Add a constructor to the Img class to implement Load image data from OpenCV Mat

* [DIP] Modify the format of the code

* [DIP] Added image encoding and decoding files, modified from opencv

* [DIP] Added image encoding and decoding files, modified from opencv

* [DIP] Modify the file format

* [DIP] Added image encoding and decoding files, modified from opencv

* [DIP] Added image encoding and decoding files, modified from opencv

* [DIP] Added the main analysis file of bmp image (read header node, read image data)

* [DIP] Added read and save functions for images

* [DIP] Modify the comments of the read and save functions for images

* [tests] Add a test image for the image container TestGrayImage.bmp

* [DIP] Add a data pointer to the img class to access image data

* [tests] Add move constructor test and copy constructor test

* [examples] Add examples of test images YuTu.bmp

* [examples][DIPDialect] replace opencv's imread and imwrite with our imread and imwrite

* [examples][DIPDialect] replace opencv's imread and imwrite with our imread and imwrite

* [examples][DIPDialect] Add the namespace dip to the Img class

* [examples][DIP] Add the namespace dip to the Img class

* [DIP] Example Modify the step setting

* [DIP] Delete the definition of a move construct

* [DIP] Delete some comments

* [DIP] Modify the comments of the file

* [DIP] Modify the grfmt_bmp.hpp file format

* [DIP] Modify the bmp image 32-bit RGB image to grayscale image conversion function

* [DIP] Img container doesn't need dip namespace

* [DIP] _type should be placed after rows and cols to match initialize order.

* [DIP] Modify the channels() function definition

* [DIP] if image decoder doesn't exist throw an error.

* [examples] Test against the old opencv

* [DIP] Remove '_' before variable name

* [DIP] add move constructor

* [DIP] remove the data member variable from the Img container, replacing it with getData()

* [DIP] Remove '_' after function name

* [DIP] unify filenames in imgcodecs,use .h as suffix,remove ////////... in the header of these reused files, which exceeds 80 column limitation.

* [DIP][examples] Remove the type definition in replenishment

* [DIP] Delete the channels member variable of the Img class

* [DIP] Modify the channels() function of the Img class

* [DIP] Modify the constructor of the Img class so that the imread function can pass parameters dynamically

* [DIP] Modify the Img constructor, replacing it with MemRef

---------

Co-authored-by: Guan-schoolmate <[email protected]>
  • Loading branch information
Guan-schoolmate and Guan-schoolmate authored Sep 21, 2023
1 parent c9f7c37 commit 830204f
Show file tree
Hide file tree
Showing 12 changed files with 2,041 additions and 136 deletions.
22 changes: 8 additions & 14 deletions examples/DIPDialect/resize2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,17 @@
//
//===----------------------------------------------------------------------===//

#include "buddy/DIP/imgcodecs/loadsave.h"
#include <buddy/Core/Container.h>
#include <buddy/DIP/DIP.h>
#include <buddy/DIP/ImageContainer.h>
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

bool testImplementation(int argc, char *argv[]) {
// Read as grayscale image.
Mat image = imread(argv[1], IMREAD_GRAYSCALE);
if (image.empty()) {
cout << "Could not read the image: " << argv[1] << endl;
}

// Define memref container for image.
Img<float, 2> input(image);
// Read as grayscale image and Define memref container for image.
Img<float, 2> input = dip::imread<float, 2>(argv[1], dip::IMGRD_GRAYSCALE);

intptr_t outputSize[2] = {250, 100}; // {image_cols, image_rows}
std::vector<float> scalingRatios = {
Expand All @@ -64,11 +57,12 @@ bool testImplementation(int argc, char *argv[]) {
// &input, dip::INTERPOLATION_TYPE::BILINEAR_INTERPOLATION,
// scalingRatios);

// Define cv::Mat with the output of Resize2D.
Mat outputImageResize2D(output.getSizes()[0], output.getSizes()[1], CV_32FC1,
output.getData());
// Define Img with the output of Resize2D.
intptr_t sizes[2] = {output.getSizes()[0], output.getSizes()[1]};

Img<float, 2> outputImageResize2D(output.getData(),sizes);

imwrite(argv[2], outputImageResize2D);
dip::imwrite(argv[2], outputImageResize2D);

return 1;
}
Expand Down
22 changes: 9 additions & 13 deletions examples/DIPDialect/rotation2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,26 @@
//
//===----------------------------------------------------------------------===//

#include "buddy/DIP/imgcodecs/loadsave.h"
#include <buddy/Core/Container.h>
#include <buddy/DIP/DIP.h>
#include <buddy/DIP/ImageContainer.h>
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

bool testImplementation(int argc, char *argv[]) {
// Read as grayscale image.
Mat image = imread(argv[1], IMREAD_GRAYSCALE);
if (image.empty()) {
cout << "Could not read the image: " << argv[1] << endl;
}
Img<float, 2> input = dip::imread<float, 2>(argv[1], dip::IMGRD_GRAYSCALE);

// Define memref containers.
Img<float, 2> input(image);
MemRef<float, 2> output = dip::Rotate2D(&input, 45, dip::ANGLE_TYPE::DEGREE);

// Define a cv::Mat with the output of Rotate2D.
Mat outputImageRotate2D(output.getSizes()[0], output.getSizes()[1], CV_32FC1,
output.getData());
imwrite(argv[2], outputImageRotate2D);
// Define a Img with the output of Rotate2D.
intptr_t sizes[2] = {output.getSizes()[0], output.getSizes()[1]};

Img<float, 2> outputImageRotate2D(output.getData(),sizes);

dip::imwrite(argv[2], outputImageRotate2D);

return 1;
}
Expand All @@ -54,4 +50,4 @@ int main(int argc, char *argv[]) {
testImplementation(argc, argv);

return 0;
}
}
Binary file added examples/images/YuTu.bmp
Binary file not shown.
193 changes: 119 additions & 74 deletions frontend/Interfaces/buddy/DIP/ImageContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,117 @@
#define FRONTEND_INTERFACES_BUDDY_DIP_IMAGECONTAINER

#include "buddy/Core/Container.h"
#include "buddy/DIP/imgcodecs/replenishment.h"
#include <cassert>
#include <opencv2/opencv.hpp>

using namespace dip;
// Image container.
// - T represents the type of the elements.
// - N represents the number of dimensions.
// - image represents the OpenCV Mat object.
// - norm indicates whether to perform normalization, and the normalization is
// disabled by default.
template <typename T, size_t N> class Img : public MemRef<T, N> {
public:
Img(){};

/**
* @brief overload
* @param sizes Array of integers specifying an n-dimensional array shape.
* @param data Pointer to the user data.
* they just initialize the matrix header that points to the specified data.
*/
Img(T *data, intptr_t sizes[N]);

/**
* @brief overload
* @param sizes Array of integers specifying an n-dimensional array shape.
*/
Img(intptr_t sizes[N]);

/**
* @brief overload
* @param m Array that (as a whole or partly) is assigned to the constructed
* matrix.
*/
Img(const Img<T, N> &m);

/**
* @brief assignment operators
* @param m Assigned, right-hand-side matrix.
* matrix.
*/
Img &operator=(const Img<T, N> &m);

// Move constructor.
Img(Img<T, N> &&m);

// Move assignment operator.
Img &operator=(Img<T, N> &&m);

/**
* @brief Load image data from OpenCV Mat.
* @param image represents the OpenCV Mat object.
* @param norm indicates whether to perform.
*/
Img(cv::Mat image, intptr_t sizes[N] = nullptr, bool norm = false);

private:
// Load image data from OpenCV Mat.
void loadImg(cv::Mat image, bool norm);
int channels();
};

/**
* @brief overload
* @param sizes Array of integers specifying an n-dimensional array shape.
*/
template <typename T, size_t N>
Img<T, N>::Img(intptr_t sizes[N]) : MemRef<T, N>(sizes) {}

/**
* @brief overload
* @param m Array that (as a whole or partly) is assigned to the constructed
* matrix.
*/
template <typename T, size_t N>
Img<T, N>::Img(const Img<T, N> &m) : MemRef<T, N>(m) {}

// Move Constructor.
// This constructor is used to initialize a MemRef object from a rvalue.
// The move constructor steals the resources of the original object.
// Note that the original object no longer owns the members and spaces.
// Steal members from the original object.
// Assign the NULL pointer to the original aligned and allocated members to
// avoid the double free error.
template <typename T, size_t N>
Img<T, N>::Img(Img<T, N> &&m) : MemRef<T, N>(m) {}

// Move Assignment Operator.
// Note that the original object no longer owns the members and spaces.
// Check if they are the same object.
// Free the data space of this object to avoid memory leaks.
// Steal members from the original object.
// Assign the NULL pointer to the original aligned and allocated members to
// avoid the double free error.
template <typename T, size_t N> Img<T, N> &Img<T, N>::operator=(Img<T, N> &&m) {
MemRef<T, N>::operator=(m);
}

/**
* @brief assignment operators
* @param m Assigned, right-hand-side matrix.
* matrix.
*/
template <typename T, size_t N>
Img<T, N> &Img<T, N>::operator=(const Img<T, N> &m) {
MemRef<T, N>::operator=(m);
}

/**
* @brief overload
* @param sizes Array of integers specifying an n-dimensional array shape.
* @param data Pointer to the user data.
* they just initialize the matrix header that points to the specified data.
*/
template <typename T, size_t N>
Img<T, N>::Img(T *data, intptr_t sizes[N]) : MemRef<T, N>(data, sizes) {}

// Image Constructor from OpenCV Mat.
template <typename T, size_t N>
Img<T, N>::Img(cv::Mat image, intptr_t sizes[N], bool norm) : MemRef<T, N>() {
Expand All @@ -60,80 +153,32 @@ Img<T, N>::Img(cv::Mat image, intptr_t sizes[N], bool norm) : MemRef<T, N>() {
this->sizes[0] = image.rows;
this->sizes[1] = image.cols;
}
// For RGB images, use NHWC layout by default.
else if (N == 4) {
this->sizes[0] = 1;
this->sizes[1] = image.rows;
this->sizes[2] = image.cols;
this->sizes[3] = 3;
}
} else {
// Use custom layout setting.
for (size_t i = 0; i < N; i++) {
this->sizes[i] = sizes[i];
}
}
this->size = this->product(this->sizes);
this->setStrides();
this->allocated = new T[this->size];
this->aligned = this->allocated;
this->loadImg(image, norm);
}

template <typename T, size_t N>
void Img<T, N>::loadImg(cv::Mat image, bool norm) {
// Load gray image data from OpenCV Mat.
if (N == 2) {
size_t k = 0;
for (int i = 0; i < this->sizes[0]; i++) {
for (int j = 0; j < this->sizes[1]; j++) {
if (norm) {
this->aligned[k] = (T)image.at<uchar>(i, j) / 255;
} else {
this->aligned[k] = (T)image.at<uchar>(i, j);
}
k++;
}
}
} else if (N == 4) {
// Detect NHWC layout of RGB image data.
if (this->sizes[1] == image.rows && this->sizes[2] == image.cols &&
this->sizes[3] == 3) {
size_t k = 0;
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
for (int color = 0; color < 3; color++) {
if (norm) {
this->aligned[k] = (T)image.at<cv::Vec3b>(i, j)[2 - color] / 255;
} else {
this->aligned[k] = (T)image.at<cv::Vec3b>(i, j)[2 - color];
}
k++;
}
}
}
}
// Detect NCHW layout of RGB image data.
else if (this->sizes[2] == image.rows && this->sizes[3] == image.cols &&
this->sizes[1] == 3) {
this->size = this->product(this->sizes);
this->setStrides();
this->allocated = new T[this->size];
this->aligned = this->allocated;
// Load gray image data from OpenCV Mat.
if (N == 2) {
size_t k = 0;
for (int color = 0; color < 3; color++) {
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
if (norm) {
this->aligned[k] = (T)image.at<cv::Vec3b>(i, j)[2 - color] / 255;
} else {
this->aligned[k] = (T)image.at<cv::Vec3b>(i, j)[2 - color];
}
k++;
for (int i = 0; i < this->sizes[0]; i++) {
for (int j = 0; j < this->sizes[1]; j++) {
if (norm) {
this->aligned[k] = (T)image.at<uchar>(i, j) / 255;
} else {
this->aligned[k] = (T)image.at<uchar>(i, j);
}
k++;
}
}
} else {
std::cerr << "RGB images must be arranged in either NHWC or NCHW layout."
<< std::endl;
}
}
}

template <typename T, size_t N> int Img<T, N>::channels() {
if (N == 2) {
return 1;
}
return this->sizes[2];
}

#endif // FRONTEND_INTERFACES_BUDDY_DIP_IMAGECONTAINER
Loading

0 comments on commit 830204f

Please sign in to comment.