C++ fast way to save image from array of values

橙三吉。 提交于 2019-12-10 11:29:51

问题


Right now, I am using CImg. I am unable to use OpenCV due to this issue.

My CImg code looks like this:

cimg_library::CImg<float> img(512,512); 
cimg_forXYC(img,x,y,c) { img(x,y,c) = (array[x][y]); } //array contains all float values between 0/1
img.save(save.c_str()); //taking a  lot of time

By using clocks I was able to determine that the first step, the for loop takes 0-0.01 seconds. However, the second step, the saving of the image, takes 0.06 seconds, which is way too long due to the amount of images I have.

I am saving as bitmaps. Is there any faster way to accomplish the same things (creating an image from an array of values and save) in C++?


回答1:


Here is a small function that will save your image in pgm format, which most things can read and is dead simple. It requires your compiler support C++11, which most do. It's also hard-coded to 512x512 images.

#include <fstream>
#include <string>
#include <cmath>
#include <cstdint>

void save_image(const ::std::string &name, float img_vals[][512])
{
   using ::std::string;
   using ::std::ios;
   using ::std::ofstream;
   typedef unsigned char pixval_t;
   auto float_to_pixval = [](float img_val) -> pixval_t {
      int tmpval = static_cast<int>(::std::floor(256 * img_val));
      if (tmpval < 0) {
         return 0u;
      } else if (tmpval > 255) {
         return 255u;
      } else {
         return tmpval & 0xffu;
      }
   };
   auto as_pgm = [](const string &name) -> string {
      if (! ((name.length() >= 4)
             && (name.substr(name.length() - 4, 4) == ".pgm")))
      {
         return name + ".pgm";
      } else {
         return name;
      }
   };

   ofstream out(as_pgm(name), ios::binary | ios::out | ios::trunc);

   out << "P5\n512 512\n255\n";
   for (int x = 0; x < 512; ++x) {
      for (int y = 0; y < 512; ++y) {
         const pixval_t pixval = float_to_pixval(img_vals[x][y]);
         const char outpv = static_cast<const char>(pixval);
         out.write(&outpv, 1);
      }
   }
}



回答2:


In a similar vein to @Omnifarious's answer, there is an extremely simple format (also based on NetPBM concepts) for float data such as yours. It is called PFM and is documented here.

The benefit is that both CImg and ImageMagick are able to read and write the format without any additional libraries, and without you needing to write any code! An additional benefit is that you retain the full tonal range of your floats, rather than just 256 steps. On the downside, you do need the full 4 bytes per pixel rather than 1 byte.

So, your code would become:

CImg<float> img(512,512); 
cimg_forXYC(img,x,y,c) { img(x,y,c) = (array[x][y]); }
img.save_pfm("filename.pfm");

I benchmarked this by creating 10,000 images and saving them to disk with the following code:

#include <iostream>
#include <cstdlib>
#define cimg_display 0        // No need for X11 stuff
#include "CImg.h"

using namespace cimg_library;
using namespace std;

#define W   512
#define H   512
#define N   10000

int main() {
    // Create and initialise float image with radial gradient
   cimg_library::CImg<float> img(W,H); 
   cimg_forXY(img,x,y) {img(x,y) = hypot((float)(W/2-x),(float)(H/2-y)); }

   char filename[128];
   for(int i=0;i<N;i++){
      sprintf(filename,"f-%06d.pfm",i);
      img.save_pfm(filename);
   }
}

It runs in 21.8 seconds, meaning 2.1 ms per image (0.002s).

As I mentioned earlier, ImageMagick is also able to handle PFM format, so you can then use GNU Parallel and ImageMagick mogrify to convert those images to JPEG:

parallel -X mogrify -format jpg -auto-level ::: *pfm

For the original 10,000 images, that takes 22 seconds, or 2.2 ms/image.



来源:https://stackoverflow.com/questions/44502079/c-fast-way-to-save-image-from-array-of-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!