Levy Barbosa
Levy Barbosa

Reputation: 442

Get all pixel data of an image in a string as quickly as possible

I need to get all the pixels data of an image in a string, with each pixel taking 6 characters, 2 for each RGB channel, i'm storing them in HEX, so the 0-255 can be written as 00-FF, so, for example, an all white pixel would be "ffffff", and an all black one "000000".

This is the code i currently have, using OpenCV, it is taking around 300ms for a 288x160 image:

Mat image = imread("image.jpg");

resize(image, image, Size(288, 160));

Vec3b buf;

stringstream ss;

auto start = high_resolution_clock::now();

for (int i = 0; i < image.rows; i++) {
    for (int j = 0; j < image.cols; j++) {
        buf = image.at<Vec3b>(i, j);
        ss << hex << setfill('0') << setw(2) << (int) buf[0] << setw(2) << (int) buf[1] << setw(2) << (int) buf[2];
    }
}

string output = ss.str();

auto stop = high_resolution_clock::now();

auto duration = duration_cast<milliseconds>(stop - start);

cout << "Execution time: " << duration.count() << " ms" << endl;

Is there any other better/faster way to do it? I know that the concept itself is not very efficient, but i really need to get a string out of it, 300ms is not that bad tbh, but the faster, the better

Upvotes: 0

Views: 283

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596387

If speed is an issue, then get rid of the stringstream altogether and just fill the string manually using some bit-shifting to calculate the hex digits, eg:

Mat image = imread("image.jpg");
resize(image, image, Size(288, 160));

const char *hexDigits = "0123456789abcdef";

auto start = high_resolution_clock::now();

string output((image.rows * image.cols) * 6, '\0');
size_t idx = 0;

for (int i = 0; i < image.rows; ++i) {
    for (int j = 0; j < image.cols; ++j) {
        Vec3b &buf = image.at<Vec3b>(i, j);
        for(int k = 0; k < 3; ++k) {
            uchar ch = buf[k];
            output[idx++] = hexDigits[(ch >> 4) & 0x0F];
            output[idx++] = hexDigits[ch & 0x0F];
        }
    }
}

auto stop = high_resolution_clock::now();

auto duration = duration_cast<milliseconds>(stop - start);

cout << "Execution time: " << duration.count() << " ms" << endl;

Alternatively, this loop might shave off a few extra milliseconds, by not having to call image.at() for every individual pixel:

for (int i = 0; i < image.rows; ++i) {
    Vec3b *buf = image.ptr<Vec3b>(i, 0);
    const Vec3b* buf_end = buf + image.cols;
    while (buf != buf_end) {
        for(int k = 0; k < 3; ++k) {
            uchar ch = (*buf)[k];
            output[idx++] = hexDigits[(ch >> 4) & 0x0F];
            output[idx++] = hexDigits[ch & 0x0F];
        }
        ++buf;
    }
}

Upvotes: 4

Related Questions