-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added code for reading and writing pngs #14
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#ifndef PNGENCODEDECODE_H | ||
#define PNGENCODEDECODE_H | ||
|
||
// C++ standard headers | ||
#include <vector> | ||
|
||
// For Cell definition | ||
#include <gillwald/geometry.hpp> | ||
|
||
namespace geometry{ | ||
|
||
namespace utility { | ||
|
||
class Image { | ||
|
||
public: | ||
|
||
// Constructs an image object that takes in the raw image data, width, and height of the image | ||
Image(std::vector<unsigned char> image, unsigned width, unsigned height): imageData_(image), width_(width), height_(height) {}; | ||
|
||
// Constructs an image object that reads a png file and updates the object fields with the information | ||
Image(const char* filename){this->decode(filename);}; | ||
|
||
// TODO: Casting operator for data to eigin matrix | ||
// operator Eigen::MatrixXi() const {}; | ||
|
||
// Returns a vector of cells that indicate the pixel values that are greater than threshVal | ||
std::vector<Cell> threshold_Cell(int threshVal); | ||
|
||
bool encode(const char* filename); | ||
|
||
void decode(const char* filename); | ||
|
||
std::vector<unsigned char> getImageData() {return this->imageData_;}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need the |
||
bool getDecodeStatus() {return this->decodeFail_;} | ||
const unsigned& width(){return this->width_;}; | ||
const unsigned& height(){return this->height_;}; | ||
|
||
private: | ||
|
||
std::vector<unsigned char> imageData_; | ||
unsigned width_; | ||
unsigned height_; | ||
bool decodeFail_ = false; | ||
|
||
}; | ||
|
||
}; | ||
}; | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
#include <util/pngEncodeDecode.hpp> | ||
#include <iostream> | ||
|
||
// Lodepng file | ||
#include <lodepng/lodepng.h> | ||
|
||
namespace geometry { | ||
|
||
namespace utility { | ||
|
||
std::vector<Cell> Image::threshold_Cell(int threshVal) { | ||
std::vector<Cell> threshVec; | ||
int count = 0; | ||
for (const auto& pixel : this->imageData_) { | ||
if (pixel >= threshVal) { | ||
threshVec.push_back({(count % (int)(this->width_)),(count / (int)(this->width_))}); | ||
} | ||
count++; | ||
} | ||
return threshVec; | ||
} | ||
|
||
bool Image::encode(const char* filename) { | ||
//Encode the image | ||
// This will encode a grayscale image. Image is expected to be size width*height. | ||
// Each value of image is expected to be from 0-255, and it rolls over | ||
unsigned error = lodepng::encode(filename, this->imageData_, this->width_, this->height_, LCT_GREY, 8); | ||
|
||
//if there's an error, display it | ||
if (error) { | ||
std::cout << "encoder error " << error << ": "<< lodepng_error_text(error) << std::endl; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove logging statements. Result is observable through return value. |
||
return false; | ||
} else { | ||
return true; | ||
} | ||
} | ||
|
||
void Image::decode(const char* filename) { | ||
// decode the image as a grayscale vector of pixels, where each pixel ranges from 0-255x | ||
unsigned error = lodepng::decode(this->imageData_, this->width_, this->height_, filename, LCT_GREY, 8); | ||
|
||
//if there's an error, display it and return false | ||
if (error) { | ||
this->decodeFail_ = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to remember the result. Just return a |
||
std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; | ||
} | ||
} | ||
|
||
}; | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// C++ Standard Library | ||
#include <vector> | ||
#include <iostream> | ||
|
||
// C Standard Library | ||
#include <stdio.h> | ||
|
||
// UNIX stuff? | ||
#include <sys/stat.h> | ||
|
||
// Gtest | ||
#include <gtest/gtest.h> | ||
#include <gmock/gmock.h> | ||
|
||
// code to test | ||
#include <util/pngEncodeDecode.hpp> | ||
|
||
using ::testing::ElementsAreArray; | ||
|
||
namespace geometry { | ||
|
||
/** | ||
* @brief Overloads the << operator to print out the Cells in a vector of cells | ||
* @param os The ostream object | ||
* @param pixels The vector of cells to be printed | ||
* @returns reference to the ostream object | ||
*/ | ||
std::ostream& operator<<(std::ostream& os, const std::vector<geometry::Cell>& pixels){ | ||
os << "\n"; | ||
for (const auto& pixel:pixels) { | ||
os << "(" << pixel.x << "," << pixel.y << ")\n"; | ||
} | ||
os << "\n"; | ||
return os; | ||
} | ||
|
||
/** | ||
* @brief Overloads the == operator to allow equality check between two Cell objects | ||
* @param lhs The cell on the left side of the == operator | ||
* @param rhs The cell on the right side of the == operator | ||
* @returns boolean on if the x and y values of the Cell are equal | ||
*/ | ||
bool operator==(const Cell &lhs,const Cell &rhs) { | ||
return (lhs.x == rhs.x) && (lhs.y == rhs.y); | ||
} | ||
|
||
namespace utility { | ||
|
||
TEST(encode_test, file_existence){ | ||
// GIVEN a file name | ||
const char* filename = "test.png"; | ||
|
||
// PREPARE by checking if the file exists, and if so deleting the file | ||
struct stat buffer; | ||
if (stat ("test.png", &buffer) == 0) {system("rm ./test.png");} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use |
||
|
||
// WHEN an image is encoded | ||
std::vector<unsigned char> imageData; | ||
unsigned width = 2, height = 2; | ||
imageData.resize(width * height); | ||
for (unsigned y = 0; y < height; y++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fill with zeros |
||
for (unsigned x = 0; x < width; x++) { | ||
imageData[(y*width) + x] = ((x+y)*64); | ||
} | ||
} | ||
|
||
Image testImage(imageData, height, width); | ||
|
||
const auto encFlag = testImage.encode(filename); | ||
|
||
// THEN check to see if the image was properly written | ||
ASSERT_TRUE(encFlag); | ||
|
||
} | ||
|
||
TEST(encode_decode_test, read_write) { | ||
// GIVEN a file name and some image data | ||
const char* filename = "test.png"; | ||
|
||
std::vector<unsigned char> imageData; | ||
unsigned width = 128, height = 4; | ||
imageData.resize(width * height); | ||
int count = 0; | ||
for (unsigned y = 0; y < height; y++) { | ||
for (unsigned x = 0; x < width; x++) { | ||
imageData[count] = count % 256; | ||
count++; | ||
} | ||
} | ||
|
||
// PREPARE by checking if the file exists, and if so deleting the file | ||
struct stat buffer; | ||
if (stat ("test.png", &buffer) == 0) {system("rm ./test.png");} | ||
|
||
// WHEN an image is succesfully encoded | ||
Image encodeImage(imageData, width, height); | ||
|
||
const auto encFlag = encodeImage.encode(filename); | ||
|
||
// THEN check to see if the image was properly written | ||
ASSERT_TRUE(encFlag); | ||
|
||
// If the image was properly written then decode it | ||
Image decodeImage(filename); | ||
|
||
// Check to see if the image was properly decoded | ||
ASSERT_FALSE(decodeImage.getDecodeStatus()); | ||
|
||
// Width check | ||
EXPECT_EQ(decodeImage.width(), 128); | ||
|
||
// Height check | ||
EXPECT_EQ(decodeImage.height(), 4); | ||
|
||
// Check to see if the decoded data matches the original data | ||
EXPECT_THAT(imageData, ElementsAreArray(decodeImage.getImageData())); | ||
|
||
} | ||
|
||
TEST(encode_decode_test, threshold_check) { | ||
// GIVEN a file name and some image data | ||
const char* filename = "test.png"; | ||
|
||
unsigned width = 8, height = 8; | ||
std::vector<unsigned char> image; | ||
int bytesPerPix = 1; | ||
image.resize(width * height * bytesPerPix); // 1 bytes per pixel | ||
int count = 0; | ||
for(unsigned y = 0; y < height; y++) { | ||
for(unsigned x = 0; x < width; x++) { | ||
count++; | ||
image[(bytesPerPix * width * y) + (bytesPerPix * x) + 0] = x*y*4 - 1; // most significant | ||
} | ||
} | ||
|
||
// PREPARE by checking if the file exists, and if so deleting the file | ||
struct stat buffer; | ||
if (stat ("test.png", &buffer) == 0) {system("rm ./test.png");} | ||
|
||
// WHEN an image is succesfully encoded | ||
Image encodeImage(image, width, height); | ||
|
||
const auto encFlag = encodeImage.encode(filename); | ||
|
||
// THEN check to see if the image was properly written | ||
ASSERT_TRUE(encFlag); | ||
|
||
// If the image was properly written then decode it | ||
Image decodeImage(filename); | ||
|
||
// Check to see if the image was properly decoded | ||
ASSERT_FALSE(decodeImage.getDecodeStatus()); | ||
|
||
// Width check | ||
EXPECT_EQ(decodeImage.width(), 8); | ||
|
||
// Height check | ||
EXPECT_EQ(decodeImage.height(), 8); | ||
|
||
// Check the cells returned by the threshold check | ||
const int threshVal = 139; | ||
const std::vector<Cell> expected{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {7, 5}, {0, 6}, {6, 6}, {7, 6}, {0, 7}, {5, 7}, {6, 7}, {7, 7}}; | ||
EXPECT_THAT(decodeImage.threshold_Cell(threshVal), ElementsAreArray(expected)); | ||
|
||
} | ||
|
||
}; | ||
|
||
}; | ||
|
||
int main(int argc, char **argv) { | ||
testing::InitGoogleTest(&argc, argv); | ||
return RUN_ALL_TESTS(); | ||
|
||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's have the header be declaration only