/** * 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 #include #include #include 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 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; }