Skip to content

Commit

Permalink
WIP: add in-memory PNG encoding/decoding for color images.
Browse files Browse the repository at this point in the history
  • Loading branch information
s-trinh committed Dec 18, 2023
1 parent a984f9e commit f9f880e
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 17 deletions.
5 changes: 5 additions & 0 deletions modules/io/include/visp3/io/vpImageIo.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,12 @@ class VISP_EXPORT vpImageIo
// int backend = IO_DEFAULT_BACKEND);
static void readPNGfromMem(const std::vector<unsigned char> &buffer, vpImage<unsigned char> &I,
int backend = IO_DEFAULT_BACKEND);
static void readPNGfromMem(const std::vector<unsigned char> &buffer, vpImage<vpRGBa> &I,
int backend = IO_DEFAULT_BACKEND);

static void writePNGtoMem(const vpImage<unsigned char> &I, std::vector<unsigned char> &buffer,
int backend = IO_DEFAULT_BACKEND);
static void writePNGtoMem(const vpImage<vpRGBa> &I, std::vector<unsigned char> &buffer,
int backend = IO_DEFAULT_BACKEND);
};
#endif
3 changes: 3 additions & 0 deletions modules/io/src/image/private/vpImageIoBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ void writePNGStb(const vpImage<unsigned char> &I, const std::string &filename);
void writePNGStb(const vpImage<vpRGBa> &I, const std::string &filename);

void readPNGfromMemStb(const std::vector<unsigned char> &buffer, vpImage<unsigned char> &I);
void readPNGfromMemStb(const std::vector<unsigned char> &buffer, vpImage<vpRGBa> &I);

void writePNGtoMemStb(const vpImage<unsigned char> &I, std::vector<unsigned char> &buffer);
void writePNGtoMemStb(const vpImage<vpRGBa> &I, std::vector<unsigned char> &buffer);

// TinyEXR lib
void writeEXRTiny(const vpImage<float> &I, const std::string &filename);
Expand Down
49 changes: 49 additions & 0 deletions modules/io/src/image/private/vpImageIoStb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
*/

#include "vpImageIoBackend.h"
#include <visp3/core/vpImageConvert.h>

#if defined __SSE2__ || defined _M_X64 || (defined _M_IX86_FP && _M_IX86_FP >= 2)
#define VISP_HAVE_SSE2 1
Expand Down Expand Up @@ -151,6 +152,26 @@ void readPNGfromMemStb(const std::vector<unsigned char> &buffer, vpImage<unsigne
delete[] buffer_read;
}

/*!
Read the content of the image bitmap stored in memory and encoded using the PNG format.
\param buffer : Color image buffer stored in RGB formar or 1D vector of unsigned char data.
\param lastPos : Size of the color image buffer.
\param I : Output decoded color image.
*/
void readPNGfromMemStb(const std::vector<unsigned char> &buffer, vpImage<vpRGBa> &I)
{
int x = 0, y = 0, comp = 0;
const int req_channels = 3;
unsigned char *buffer_read = stbi_load_from_memory(buffer.data(), buffer.size(), &x, &y, &comp, req_channels);

I.init(y, x);
const bool flip = false;
vpImageConvert::RGBToRGBa(buffer_read, reinterpret_cast<unsigned char *>(I.bitmap), x, y, flip);

delete[] buffer_read;
}

void writePNGtoMemStb(const vpImage<unsigned char> &I, std::vector<unsigned char> &buffer)
{
const int height = I.getRows();
Expand All @@ -173,3 +194,31 @@ void writePNGtoMemStb(const vpImage<unsigned char> &I, std::vector<unsigned char
throw(vpImageException(vpImageException::ioError, message));
}
}

void writePNGtoMemStb(const vpImage<vpRGBa> &I, std::vector<unsigned char> &buffer)
{
const int height = I.getRows();
const int width = I.getCols();
const int channels = 3;

custom_stbi_mem_context context;
context.last_pos = 0;
buffer.resize(I.getHeight() * I.getWidth() * channels);
context.context = (void *)buffer.data();

unsigned char *bitmap = new unsigned char[height * width * channels];
vpImageConvert::RGBaToRGB(reinterpret_cast<unsigned char *>(I.bitmap), bitmap, height*width);

const int stride_bytes = 0;
int result = stbi_write_png_to_func(custom_stbi_write_mem, &context, width, height, channels, bitmap, stride_bytes);

delete[] bitmap;

if (result) {
buffer.resize(context.last_pos);
}
else {
std::string message = "Cannot write png to memory, result: " + std::to_string(result);
throw(vpImageException(vpImageException::ioError, message));
}
}
10 changes: 10 additions & 0 deletions modules/io/src/image/vpImageIo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,11 @@ void vpImageIo::readPNGfromMem(const std::vector<unsigned char> &buffer, vpImage
}
}

void vpImageIo::readPNGfromMem(const std::vector<unsigned char> &buffer, vpImage<vpRGBa> &I, int backend)
{
readPNGfromMemStb(buffer, I);
}

void vpImageIo::writePNGtoMem(const vpImage<unsigned char> &I, std::vector<unsigned char> &buffer, int backend)
{
if (backend == IO_DEFAULT_BACKEND) {
Expand Down Expand Up @@ -1128,3 +1133,8 @@ void vpImageIo::writePNGtoMem(const vpImage<unsigned char> &I, std::vector<unsig
writePNGtoMemStb(I, buffer);
}
}

void vpImageIo::writePNGtoMem(const vpImage<vpRGBa> &I, std::vector<unsigned char> &buffer, int backend)
{
writePNGtoMemStb(I, buffer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,24 @@ std::string toString(const std::string &name, int val)
}
}

int main(int argc, char **argv)
int main(int argc, char *argv[])
{
#if defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(HAVE_OPENCV_HIGHGUI)
bool opencv_backend = false;
std::string npz_filename = "npz_tracking_teabox.npz";

for (int i = 1; i < argc; i++) {
if (std::string(argv[i]) == "--cv-backend") {
opencv_backend = true;
}
else if ((std::string(argv[i]) == "--input" || std::string(argv[i]) == "-i") && i+1 < argc) {
npz_filename = argv[i+1];
}
}

std::cout << "Input file: " << npz_filename << std::endl;
std::cout << "OpenCV backend? " << opencv_backend << std::endl;

const std::string npz_filename = "npz_tracking_teabox.npz";
visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);

visp::cnpy::NpyArray arr_height = npz_data["height"];
Expand All @@ -53,9 +60,16 @@ int main(int argc, char **argv)
std::cout << "Cam: " << cam << std::endl;

vpImage<unsigned char> I(height, width);
vpImage<vpRGBa> I_display;
vpImageConvert::convert(I, I_display);
vpDisplayX d(I_display, 100, 100, "Model-based tracker");
vpImage<vpRGBa> I_display(height, width);

std::unique_ptr<vpDisplay> display;
#if defined(VISP_HAVE_X11)
display = std::make_unique<vpDisplayX>(I_display, 100, 100, "Model-based tracker");
#elif defined(VISP_HAVE_GDI)
display = std::make_unique<vpDisplayGDI>(I_display, 100, 100, "Model-based tracker");
#elif defined(HAVE_OPENCV_HIGHGUI)
display = std::make_unique<vpDisplayOpenCV>(I_display, 100, 100, "Model-based tracker");
#endif

visp::cnpy::NpyArray arr_nb_data = npz_data["nb_data"];
int nb_data = *arr_nb_data.data<int>();
Expand Down Expand Up @@ -87,12 +101,13 @@ int main(int argc, char **argv)
vpImageIo::readPNGfromMem(vec_img, I, vpImageIo::IO_OPENCV_BACKEND);
}
else {
vpImageIo::readPNGfromMem(vec_img, I, vpImageIo::IO_STB_IMAGE_BACKEND);
// vpImageIo::readPNGfromMem(vec_img, I, vpImageIo::IO_STB_IMAGE_BACKEND);
vpImageIo::readPNGfromMem(vec_img, I_display, vpImageIo::IO_STB_IMAGE_BACKEND);
}
double end = vpTime::measureTimeMs();
times.push_back(end-start);
img_data_offset += vec_img_data_size_ptr[iter];
vpImageConvert::convert(I, I_display);
// vpImageConvert::convert(I, I_display);

const std::string str_model_iter_sz = toString("model_%06d", iter) + "_sz";
visp::cnpy::NpyArray arr_model_iter_sz = npz_data[str_model_iter_sz];
Expand Down Expand Up @@ -132,5 +147,9 @@ int main(int argc, char **argv)

vpDisplay::getClick(I_display, true);

return EXIT_SUCCESS;
#else
std::cerr << "Error, a missing display library is needed (X11, GDI or OpenCV with HighGUI module)."
#endif

return EXIT_SUCCESS;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ std::string toString(const std::string &name, int val)
int main(int argc, char **argv)
{
bool opencv_backend = false;
std::string npz_filename = "npz_tracking_teabox.npz";

for (int i = 1; i < argc; i++) {
if (std::string(argv[i]) == "--cv-backend") {
opencv_backend = true;
}
else if ((std::string(argv[i]) == "--input" || std::string(argv[i]) == "-i") && i+1 < argc) {
npz_filename = argv[i+1];
}
}

std::cout << "Input file: " << npz_filename << std::endl;
std::cout << "OpenCV backend? " << opencv_backend << std::endl;

std::string opt_videoname = "model/teabox/teabox.mp4";
Expand All @@ -49,6 +56,7 @@ int main(int argc, char **argv)
std::cout << "Video name: " << opt_videoname << std::endl;

vpImage<unsigned char> I;
vpImage<vpRGBa> I_color;

vpVideoReader g;
g.setFileName(opt_videoname);
Expand All @@ -70,12 +78,12 @@ int main(int argc, char **argv)

const int height = I.getRows();
const int width = I.getCols();
const int channel = 1;
// const int channel = 1;
const int channel = 3;

std::vector<unsigned char> img_buffer;
// img_buffer.resize(height * width * channel);

const std::string npz_filename = "npz_tracking_teabox.npz";
const std::string camera_name = "Camera";
std::vector<char> vec_camera_name(camera_name.begin(), camera_name.end());

Expand Down Expand Up @@ -112,7 +120,9 @@ int main(int argc, char **argv)
vpImageIo::writePNGtoMem(I, img_buffer, vpImageIo::IO_OPENCV_BACKEND);
}
else {
vpImageIo::writePNGtoMem(I, img_buffer, vpImageIo::IO_STB_IMAGE_BACKEND);
vpImageConvert::convert(I, I_color);
// vpImageIo::writePNGtoMem(I, img_buffer, vpImageIo::IO_STB_IMAGE_BACKEND);
vpImageIo::writePNGtoMem(I_color, img_buffer, vpImageIo::IO_STB_IMAGE_BACKEND);
}
double end = vpTime::measureTimeMs();
times.push_back(end-start);
Expand Down Expand Up @@ -155,12 +165,6 @@ int main(int argc, char **argv)
std::cout << "Mean time: " << vpMath::getMean(times) << " ms ; Median time: "
<< vpMath::getMedian(times) << " ms ; Std: " << vpMath::getStdev(times) << " ms" << std::endl;

// visp::cnpy::npz_save(npz_filename, "vec_img_data_size", &vec_img_data_size[0], { vec_img_data_size.size() }, "a");
// visp::cnpy::npz_save(npz_filename, "vec_img", &vec_img_data[0], { vec_img_data.size() }, "a");
// std::cout << "vec_img_data.size()=" << vec_img_data.size() << std::endl;
// // Show how to save a "multidimensional" array
// visp::cnpy::npz_save(npz_filename, "vec_poses", &vec_poses[0], { static_cast<size_t>(iter), 6 }, "a");

visp::cnpy::npz_save(npz_filename, "vec_img_data_size", vec_img_data_size.data(), { vec_img_data_size.size() }, "a");
visp::cnpy::npz_save(npz_filename, "vec_img", vec_img_data.data(), { vec_img_data.size() }, "a");
// Show how to save a "multidimensional" array
Expand Down

0 comments on commit f9f880e

Please sign in to comment.