问题
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