forked from openvinotoolkit/openvino
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
301 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
|
||
namespace deepworks { | ||
|
||
class Tensor; | ||
|
||
namespace io { | ||
deepworks::Tensor ReadImage(std::string_view); | ||
} // namespace io | ||
|
||
} // namespace deepworks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
#include <stdexcept> | ||
#include <sstream> | ||
#include <deepworks/tensor.hpp> | ||
#include "util/assert.hpp" | ||
|
||
#ifdef HAVE_JPEG | ||
#include <jpeglib.h> | ||
#endif | ||
|
||
#ifdef HAVE_PNG | ||
#include <png.h> | ||
#include <algorithm> | ||
|
||
#endif | ||
|
||
namespace { | ||
bool IsPngFile(std::string_view path) { | ||
return path.substr(path.find_last_of(".") + 1) == "png"; | ||
} | ||
|
||
bool IsJpegFile(std::string_view path) { | ||
return path.substr(path.find_last_of(".") + 1) == "jpg" || path.substr(path.find_last_of(".") + 1) == "jpeg"; | ||
} | ||
|
||
deepworks::Tensor ReadJpegFile(std::string_view path) { | ||
#ifdef HAVE_JPEG | ||
struct jpeg_decompress_struct cinfo{}; | ||
struct jpeg_error_mgr err{}; | ||
FILE *infile = fopen(path.data(), "rb"); | ||
|
||
if (!infile) { | ||
std::stringstream fmt; | ||
fmt << "can't open file: " << path; | ||
DeepWorks_Assert(false && fmt.str().c_str()); | ||
} | ||
|
||
cinfo.err = jpeg_std_error(&err); | ||
jpeg_create_decompress(&cinfo); | ||
jpeg_stdio_src(&cinfo, infile); | ||
|
||
(void) jpeg_read_header(&cinfo, true); | ||
(void) jpeg_start_decompress(&cinfo); | ||
|
||
size_t row_stride = cinfo.output_width * cinfo.output_components; | ||
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); | ||
|
||
int width = static_cast<int>(cinfo.output_width); | ||
int height = static_cast<int>(cinfo.output_height); | ||
int channels = static_cast<int>(cinfo.output_components); | ||
|
||
deepworks::Tensor out_tensor(deepworks::Shape{height, width, channels}); | ||
|
||
deepworks::Tensor::Type *dst_data = out_tensor.data(); | ||
deepworks::Strides tensor_strides = out_tensor.strides(); | ||
|
||
const size_t elements_per_h_channel = width * channels; | ||
size_t h = 0; | ||
// it's works if we have default hwc layout for tensor | ||
while (cinfo.output_scanline < cinfo.output_height) { | ||
(void) jpeg_read_scanlines(&cinfo, buffer, 1); | ||
std::copy_n(buffer[0], elements_per_h_channel, &dst_data[h * tensor_strides[0]]); | ||
++h; | ||
} | ||
|
||
(void) jpeg_finish_decompress(&cinfo); | ||
jpeg_destroy_decompress(&cinfo); | ||
fclose(infile); | ||
return out_tensor; | ||
#else | ||
DeepWorks_Assert(false && "Couldn't find LIBJPEG"); | ||
return {}; | ||
#endif | ||
} | ||
|
||
deepworks::Tensor ReadPngFile(std::string_view path) { | ||
#ifdef HAVE_PNG | ||
FILE *infile = fopen(path.data(), "rb"); | ||
if (!infile) { | ||
std::stringstream fmt; | ||
fmt << "can't open file: " << path; | ||
DeepWorks_Assert(false && fmt.str().c_str()); | ||
} | ||
char header[8]; | ||
fread(header, 1, 8, infile); | ||
if (png_sig_cmp(reinterpret_cast<png_const_bytep>(header), 0, 8)) { | ||
std::stringstream fmt; | ||
fmt << "File is not a PNG file: " << path; | ||
DeepWorks_Assert(false && fmt.str().c_str()); | ||
} | ||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | ||
|
||
png_infop info_ptr = png_create_info_struct(png_ptr); | ||
|
||
DeepWorks_Assert(!setjmp(png_jmpbuf(png_ptr)) && "Error during init_io"); | ||
|
||
png_init_io(png_ptr, infile); | ||
png_set_sig_bytes(png_ptr, 8); | ||
|
||
png_read_info(png_ptr, info_ptr); | ||
|
||
int width = static_cast<int>(png_get_image_width(png_ptr, info_ptr)); | ||
int height = static_cast<int>(png_get_image_height(png_ptr, info_ptr)); | ||
int channels = static_cast<int>(png_get_channels(png_ptr, info_ptr)); | ||
|
||
png_read_update_info(png_ptr, info_ptr); | ||
|
||
deepworks::Tensor out_tensor(deepworks::Shape{height, width, channels}); | ||
|
||
deepworks::Tensor::Type *dst_data = out_tensor.data(); | ||
deepworks::Strides tensor_strides = out_tensor.strides(); | ||
/* read file */ | ||
DeepWorks_Assert(!setjmp(png_jmpbuf(png_ptr)) && "Error during read image"); | ||
|
||
auto *row_pointers = (png_bytep *) malloc(sizeof(png_bytep) * height); | ||
for (int y = 0; y < height; y++) { | ||
row_pointers[y] = (png_byte *) malloc(png_get_rowbytes(png_ptr, info_ptr)); | ||
} | ||
png_read_image(png_ptr, row_pointers); | ||
// it's works if we have default hwc layout for tensor | ||
const size_t elements_per_h_channel = width * channels; | ||
for (int h = 0; h < height; ++h) { | ||
std::copy_n(row_pointers[h], elements_per_h_channel, &dst_data[h * tensor_strides[0]]); | ||
} | ||
for (int y = 0; y < height; y++) { | ||
free(row_pointers[y]); | ||
} | ||
free(row_pointers); | ||
fclose(infile); | ||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL); | ||
return out_tensor; | ||
#else | ||
DeepWorks_Assert(false && "Couldn't find LIBPNG"); | ||
return {}; | ||
#endif | ||
} | ||
} | ||
|
||
namespace deepworks::io { | ||
Tensor ReadImage(std::string_view path) { | ||
if (IsPngFile(path)) { | ||
return ReadPngFile(path); | ||
} | ||
if (IsJpegFile(path)) { | ||
return ReadJpegFile(path); | ||
} | ||
DeepWorks_Assert(false && "image format not supported"); | ||
return {}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
#include <gtest/gtest.h> | ||
#include <fstream> | ||
|
||
#include <deepworks/shape.hpp> | ||
#include <deepworks/tensor.hpp> | ||
#include <deepworks/image_reader.hpp> | ||
#include <numeric> | ||
|
||
#include "test_utils.hpp" | ||
|
||
namespace { | ||
// the reference image data was written in H, W, C loop | ||
deepworks::Tensor GetTensorFromBinary(std::istream &expected_stream, const deepworks::Shape& shape) { | ||
deepworks::Tensor tensor(shape); | ||
const size_t total_elements = tensor.total(); | ||
auto it = std::istreambuf_iterator<char>(expected_stream); | ||
// copy works only when the layout of the reference and tested tensor matches | ||
|
||
auto* tensor_dst = tensor.data(); | ||
for (size_t index = 0; index < total_elements; ++index) { | ||
tensor_dst[index] = static_cast<uint8_t>(*it); | ||
++it; | ||
} | ||
return tensor; | ||
} | ||
} | ||
|
||
TEST(ImageReader, ReadRGBPng) { | ||
std::string image_path = deepworks::testutils::GetTestDataPath(); | ||
image_path += "/image/lenna.png"; | ||
std::string reference_path = deepworks::testutils::GetTestDataPath(); | ||
reference_path += "/image/lenna_reference.bin"; | ||
|
||
const deepworks::Tensor actual_tensor = deepworks::io::ReadImage(image_path); | ||
const auto expected_shape = deepworks::Shape{512, 512, 3}; | ||
|
||
std::fstream stream(reference_path, std::ios_base::binary | std::ios_base::in); | ||
auto expected_tensor = GetTensorFromBinary(stream, expected_shape); | ||
|
||
deepworks::testutils::AssertTensorEqual(actual_tensor, expected_tensor); | ||
} | ||
|
||
TEST(ImageReader, ReadTransparentPng) { | ||
std::string image_path = deepworks::testutils::GetTestDataPath(); | ||
image_path += "/image/transparent.png"; | ||
std::string reference_path = deepworks::testutils::GetTestDataPath(); | ||
reference_path += "/image/transparent_reference.bin"; | ||
|
||
const deepworks::Tensor actual_tensor = deepworks::io::ReadImage(image_path); | ||
const auto expected_shape = deepworks::Shape{600, 800, 4}; | ||
|
||
std::fstream stream(reference_path, std::ios_base::binary | std::ios_base::in); | ||
auto expected_tensor = GetTensorFromBinary(stream, expected_shape); | ||
|
||
deepworks::testutils::AssertTensorEqual(actual_tensor, expected_tensor); | ||
} | ||
|
||
TEST(ImageReader, ReadRGBJPEG) { | ||
std::string image_path = deepworks::testutils::GetTestDataPath(); | ||
image_path += "/image/sunset.jpg"; | ||
std::string reference_path = deepworks::testutils::GetTestDataPath(); | ||
reference_path += "/image/sunset_reference.bin"; | ||
|
||
const deepworks::Tensor actual_tensor = deepworks::io::ReadImage(image_path); | ||
const auto expected_shape = deepworks::Shape{600, 800, 3}; | ||
|
||
std::fstream stream(reference_path, std::ios_base::binary | std::ios_base::in); | ||
auto expected_tensor = GetTensorFromBinary(stream, expected_shape); | ||
|
||
deepworks::testutils::AssertTensorEqual(actual_tensor, expected_tensor); | ||
} | ||
|
||
TEST(ImageReader, ReadGrayScaleJPEG) { | ||
std::string image_path = deepworks::testutils::GetTestDataPath(); | ||
image_path += "/image/grayscale.jpg"; | ||
std::string reference_path = deepworks::testutils::GetTestDataPath(); | ||
reference_path += "/image/grayscale_reference.bin"; | ||
|
||
const deepworks::Tensor actual_tensor = deepworks::io::ReadImage(image_path); | ||
const auto expected_shape = deepworks::Shape{600, 600, 1}; | ||
|
||
std::fstream stream(reference_path, std::ios_base::binary | std::ios_base::in); | ||
auto expected_tensor = GetTensorFromBinary(stream, expected_shape); | ||
|
||
deepworks::testutils::AssertTensorEqual(actual_tensor, expected_tensor); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
|
||
namespace deepworks::testutils { | ||
std::string GetTestDataPath() { | ||
return TEST_DATA_PATH; | ||
} | ||
|
||
void AssertTensorEqual(const deepworks::Tensor& actual, const deepworks::Tensor& expected) { | ||
ASSERT_EQ(actual.shape() , expected.shape()); | ||
ASSERT_EQ(actual.strides(), expected.strides()); | ||
|
||
auto* actual_p = actual.data(); | ||
auto* expected_p = expected.data(); | ||
|
||
auto total = actual.total(); | ||
for (int i = 0; i < total; ++i) { | ||
ASSERT_FLOAT_EQ(expected_p[i], actual_p[i]); | ||
} | ||
} | ||
} // deepworks::testutils |