optimization: streamline image loading and remove pixel vector
- Read file once and decode from memory using stbi_load_from_memory - Add guard for files smaller than 8 bytes - Remove std::vector<Pixel> and accumulate histogram in downsample loop
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <cstdio>
|
||||
|
||||
using std::cout;
|
||||
@@ -15,10 +16,6 @@ namespace fs = std::filesystem;
|
||||
bool is_jpeg(const unsigned char* buf);
|
||||
bool is_png(const unsigned char* buf);
|
||||
|
||||
struct Pixel {
|
||||
unsigned char r, g, b;
|
||||
};
|
||||
|
||||
struct Bucket {
|
||||
long r = 0;
|
||||
long g = 0;
|
||||
@@ -62,22 +59,30 @@ int main(int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned char header[8];
|
||||
file.read(reinterpret_cast<char*>(header), 8);
|
||||
// Read the file once; detect the format from the buffer and decode from
|
||||
// memory so the image is never read from disk a second time.
|
||||
std::vector<unsigned char> buffer((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
if (buffer.size() < 8) {
|
||||
cout << "File is too small to be a valid image!\n";
|
||||
return 1;
|
||||
}
|
||||
const unsigned char* header = buffer.data();
|
||||
|
||||
if (is_png(header)) {
|
||||
cout << "PNG file detected!\n";
|
||||
} else if (is_jpeg(header)) {
|
||||
cout << "JPEG file detected!\n";
|
||||
} else {
|
||||
cout << "Unkown image file format!\n";
|
||||
cout << "Unknown image file format!\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
int width, height, channels;
|
||||
|
||||
unsigned char* data = stbi_load(
|
||||
argv[1],
|
||||
unsigned char* data = stbi_load_from_memory(
|
||||
buffer.data(),
|
||||
static_cast<int>(buffer.size()),
|
||||
&width,
|
||||
&height,
|
||||
&channels,
|
||||
@@ -97,46 +102,42 @@ int main(int argc, char *argv[]) {
|
||||
cout << "Channels: " << channels << "\n";
|
||||
cout << "**********\n";
|
||||
|
||||
std::vector<Pixel> samples;
|
||||
|
||||
int step = 10; //downsample rate
|
||||
for (int y = 0; y< height; y += step) {
|
||||
for (int x = 0; x < width; x+= step) {
|
||||
int idx = (y * width + x) * 3;
|
||||
|
||||
Pixel p;
|
||||
p.r = data[idx];
|
||||
p.g = data[idx + 1];
|
||||
p.b = data[idx + 2];
|
||||
samples.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
cout << "Sampled pixels: " << samples.size() << "\n";
|
||||
|
||||
std::unordered_map<int, Bucket> hist;
|
||||
|
||||
for (auto& p : samples) {
|
||||
int brightness = p.r + p.g + p.b;
|
||||
int step = 10; //downsample rate
|
||||
long sampled = 0;
|
||||
for (int y = 0; y < height; y += step) {
|
||||
for (int x = 0; x < width; x += step) {
|
||||
int idx = (y * width + x) * 3;
|
||||
|
||||
unsigned char r = data[idx];
|
||||
unsigned char g = data[idx + 1];
|
||||
unsigned char b = data[idx + 2];
|
||||
sampled++;
|
||||
|
||||
int brightness = r + g + b;
|
||||
if (brightness < 60) continue; //filtering out dark pixels
|
||||
|
||||
int minc = std::min({p.r, p.g, p.b});
|
||||
int maxc = std::max({p.r, p.g, p.b});
|
||||
int minc = std::min({r, g, b});
|
||||
int maxc = std::max({r, g, b});
|
||||
if (maxc - minc < 10) continue; //filtering out low saturation
|
||||
//(gray) colors
|
||||
|
||||
int rq = p.r / 16;
|
||||
int gq = p.g / 16;
|
||||
int bq = p.b / 16;
|
||||
int rq = r / 16;
|
||||
int gq = g / 16;
|
||||
int bq = b / 16;
|
||||
|
||||
int key = (rq << 8) | (gq << 4) | bq;
|
||||
|
||||
auto& bucket = hist[key];
|
||||
bucket.r += p.r;
|
||||
bucket.g += p.g;
|
||||
bucket.b += p.b;
|
||||
bucket.count ++;
|
||||
bucket.r += r;
|
||||
bucket.g += g;
|
||||
bucket.b += b;
|
||||
bucket.count++;
|
||||
}
|
||||
}
|
||||
|
||||
cout << "Sampled pixels: " << sampled << "\n";
|
||||
|
||||
std::vector<Bucket> buckets;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user