60 lines
1.6 KiB
C++
60 lines
1.6 KiB
C++
/**
|
|
* AWB Processor - OpenCV Gray World White Balance
|
|
* Compiled to WASM via Emscripten (MAIN_MODULE, dynamic linking with .so)
|
|
*
|
|
* Reads: /data/input.png (MEMFS, injected by Node.js runner)
|
|
* Writes: /data/output.png (MEMFS, read by Node.js runner)
|
|
* Stdout: metadata string
|
|
*/
|
|
#include <stdio.h>
|
|
#include <opencv2/core.hpp>
|
|
#include <opencv2/imgproc.hpp>
|
|
#include <opencv2/imgcodecs.hpp>
|
|
|
|
int main() {
|
|
const char* input = "/data/input.png";
|
|
const char* output = "/data/output.png";
|
|
|
|
cv::Mat img = cv::imread(input);
|
|
if (img.empty()) {
|
|
fprintf(stderr, "ERR:Failed to read: %s\n", input);
|
|
return 1;
|
|
}
|
|
|
|
int h = img.rows, w = img.cols;
|
|
|
|
// Convert to float for accuracy
|
|
cv::Mat imgf;
|
|
img.convertTo(imgf, CV_32FC3);
|
|
|
|
// Split channels (BGR order in OpenCV)
|
|
std::vector<cv::Mat> ch;
|
|
cv::split(imgf, ch);
|
|
|
|
// Compute channel means
|
|
cv::Scalar m = cv::mean(imgf);
|
|
|
|
// Gray World: average of all channels
|
|
float avg = (m[0] + m[1] + m[2]) / 3.0f;
|
|
|
|
// Scale each channel so its mean equals the global average
|
|
if (m[0] > 0.01f) ch[0] *= (avg / m[0]); // B
|
|
if (m[1] > 0.01f) ch[1] *= (avg / m[1]); // G
|
|
if (m[2] > 0.01f) ch[2] *= (avg / m[2]); // R
|
|
|
|
// Merge and convert back
|
|
cv::merge(ch, imgf);
|
|
imgf.convertTo(img, CV_8UC3);
|
|
|
|
// Write output
|
|
cv::imwrite(output, img);
|
|
|
|
// Metadata to stderr (runner reads pure base64 from stdout)
|
|
fprintf(stderr, "OK:%dx%d:M=%.2f,%.2f,%.2f:S=%.4f,%.4f,%.4f\n",
|
|
w, h,
|
|
m[2], m[1], m[0], // RGB means
|
|
avg / m[2], avg / m[1], avg / m[0]); // RGB scales
|
|
|
|
return 0;
|
|
}
|