14.8: Camera Raw's colour noise reduction keeps brightness

This commit is contained in:
ZacharyZhang-NY committed 2026-10-11 05:54:59 -04:00
1 parent 00876282a5
commit 440bd1c9b0
4 files changed
+197 -21

No files matched your search

+41 -21
View File
@@ -936,47 +936,67 @@ void adjust_camera_raw_detail(uint8_t *rgba, size_t width, size_t height, size_t
}
}
if (noiseColor > 0) {
int radius = effects_radius(1.0 + noiseColorSmoothness / 40.0, scale);
float *chroma = malloc(count * sizeof(float));
float *chromaBlur = malloc(count * sizeof(float));
if (!chroma || !chromaBlur) { free(chroma); free(chromaBlur); free(luma); free(work); return; }
// Color noise is speckle of random hue over the picture's own color, in blotches a few pixels wide. Each pixel
// keeps its brightness and takes the color of the area around it: its two color differences from its
// brightness, red and blue, are blurred and put back over the brightness unchanged. Blurring saturation
// alone kept every speck's own hue, so the speckle stayed.
double strength = noiseColor / 100.0;
int radius = effects_radius(1.5 + strength * 4.0 * (0.5 + noiseColorSmoothness / 100.0), scale);
float *planes = malloc(count * sizeof(float) * 6);
if (!planes) { free(luma); free(work); return; }
// Red and blue differences and coverage, then the same blurred. Each pixel counts by its alpha, so clear
// pixels add no color of their own and don't pull the colors beside them toward gray.
float *red = planes, *blue = planes + count, *weight = planes + 2 * count;
float *redBlur = planes + 3 * count, *blueBlur = planes + 4 * count, *weightBlur = planes + 5 * count;
for (size_t y = 0; y < height; ++y) {
uint8_t *row = rgba + y * stride;
for (size_t x = 0; x < width; ++x) {
uint8_t *p = row + x * 4;
size_t index = y * width + x;
double alpha = p[3];
if (!alpha) continue;
if (!alpha) { red[index] = 0; blue[index] = 0; weight[index] = 0; continue; }
double r = fmin(1.0, p[0] / alpha), g = fmin(1.0, p[1] / alpha), b = fmin(1.0, p[2] / alpha);
double h, s, l;
rgb_to_hsl(r, g, b, &h, &s, &l);
chroma[y * width + x] = (float)s;
double luminance = rec709(r, g, b), coverage = alpha / 255.0;
red[index] = (float)((r - luminance) * coverage);
blue[index] = (float)((b - luminance) * coverage);
weight[index] = (float)coverage;
}
}
if (!box_blur_plane(chroma, chromaBlur, width, height, radius)) {
free(chroma); free(chromaBlur); free(luma); free(work); return;
if (!box_blur_plane(red, redBlur, width, height, radius) || !box_blur_plane(blue, blueBlur, width, height, radius)
|| !box_blur_plane(weight, weightBlur, width, height, radius)) {
free(planes); free(luma); free(work); return;
}
double strength = noiseColor / 100.0;
for (size_t i = 0; i < count; ++i) {
if (weightBlur[i] > 1e-6f) { redBlur[i] /= weightBlur[i]; blueBlur[i] /= weightBlur[i]; }
}
// Color Detail keeps real color edges: where the smoothed color itself changes across a blur's width. Grain
// doesn't count, as it did when each pixel was compared with its own blur.
double preserve = noiseColorDetail / 100.0;
size_t reach = (size_t)radius;
for (size_t y = 0; y < height; ++y) {
uint8_t *row = rgba + y * stride;
size_t up = y >= reach ? y - reach : 0, down = y + reach < height ? y + reach : height - 1;
for (size_t x = 0; x < width; ++x) {
uint8_t *p = row + x * 4;
double alpha = p[3];
if (!alpha) continue;
size_t index = y * width + x;
float edge = fabsf(chroma[index] - chromaBlur[index]);
double local = strength * (1.0 - preserve * fmin(1.0, edge * 4.0));
float sat = chroma[index] * (float)(1.0 - local) + chromaBlur[index] * (float)local;
size_t left = x >= reach ? x - reach : 0, right = x + reach < width ? x + reach : width - 1;
double dx = fabs(redBlur[y * width + right] - redBlur[y * width + left]) + fabs(blueBlur[y * width + right] - blueBlur[y * width + left]);
double dy = fabs(redBlur[down * width + x] - redBlur[up * width + x]) + fabs(blueBlur[down * width + x] - blueBlur[up * width + x]);
double local = strength * (1.0 - preserve * fmin(1.0, (dx + dy) * 3.0));
double r = fmin(1.0, p[0] / alpha), g = fmin(1.0, p[1] / alpha), b = fmin(1.0, p[2] / alpha);
double h, s, l;
rgb_to_hsl(r, g, b, &h, &s, &l);
s = sat;
hsl_to_rgb(h, s, l, &r, &g, &b);
write_premultiplied(p, r, g, b, alpha);
double luminance = rec709(r, g, b);
double redDiff = (r - luminance) + (redBlur[index] - (r - luminance)) * local;
double blueDiff = (b - luminance) + (blueBlur[index] - (b - luminance)) * local;
r = luminance + redDiff;
b = luminance + blueDiff;
// Green follows from the brightness kept and the new red and blue.
g = (luminance - 0.2126 * r - 0.0722 * b) / 0.7152;
write_premultiplied(p, fmin(1.0, fmax(0.0, r)), fmin(1.0, fmax(0.0, g)), fmin(1.0, fmax(0.0, b)), alpha);
}
}
free(chroma);
free(chromaBlur);
free(planes);
}
if (sharpenAmount > 0) {
for (size_t y = 0; y < height; ++y) {