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