Skip to content

Commit

Permalink
fix: Clamp pixel colors when using a noise filter
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor-pelykh committed Oct 6, 2023
1 parent 76e556e commit 62353fe
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 18 deletions.
78 changes: 63 additions & 15 deletions src/filter/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1873,9 +1873,21 @@ export abstract class Filter {
switch (type) {
case NoiseType.gaussian:
for (const p of frame) {
const r = p.r + nSigma * RandomUtils.grand();
const g = p.g + nSigma * RandomUtils.grand();
const b = p.b + nSigma * RandomUtils.grand();
const r = MathUtils.clamp(
p.r + nSigma * RandomUtils.grand(),
0,
p.maxChannelValue
);
const g = MathUtils.clamp(
p.g + nSigma * RandomUtils.grand(),
0,
p.maxChannelValue
);
const b = MathUtils.clamp(
p.b + nSigma * RandomUtils.grand(),
0,
p.maxChannelValue
);
const a = p.a;
const msk = opt.mask
?.getPixel(p.x, p.y)
Expand All @@ -1892,9 +1904,21 @@ export abstract class Filter {
break;
case NoiseType.uniform:
for (const p of frame) {
const r = p.r + nSigma * RandomUtils.crand();
const g = p.g + nSigma * RandomUtils.crand();
const b = p.b + nSigma * RandomUtils.crand();
const r = MathUtils.clamp(
p.r + nSigma * RandomUtils.crand(),
0,
p.maxChannelValue
);
const g = MathUtils.clamp(
p.g + nSigma * RandomUtils.crand(),
0,
p.maxChannelValue
);
const b = MathUtils.clamp(
p.b + nSigma * RandomUtils.crand(),
0,
p.maxChannelValue
);
const a = p.a;
const msk = opt.mask
?.getPixel(p.x, p.y)
Expand All @@ -1919,9 +1943,21 @@ export abstract class Filter {
}
for (const p of frame) {
if (Math.random() * 100 < nSigma) {
const r = Math.random() < 0.5 ? max : min;
const g = Math.random() < 0.5 ? max : min;
const b = Math.random() < 0.5 ? max : min;
const r = MathUtils.clamp(
Math.random() < 0.5 ? max : min,
0,
p.maxChannelValue
);
const g = MathUtils.clamp(
Math.random() < 0.5 ? max : min,
0,
p.maxChannelValue
);
const b = MathUtils.clamp(
Math.random() < 0.5 ? max : min,
0,
p.maxChannelValue
);
const a = p.a;
const msk = opt.mask
?.getPixel(p.x, p.y)
Expand All @@ -1939,9 +1975,21 @@ export abstract class Filter {
break;
case NoiseType.poisson:
for (const p of frame) {
const r = RandomUtils.prand(p.r);
const g = RandomUtils.prand(p.g);
const b = RandomUtils.prand(p.b);
const r = MathUtils.clamp(
RandomUtils.prand(p.r),
0,
p.maxChannelValue
);
const g = MathUtils.clamp(
RandomUtils.prand(p.g),
0,
p.maxChannelValue
);
const b = MathUtils.clamp(
RandomUtils.prand(p.b),
0,
p.maxChannelValue
);
const a = p.a;
const msk = opt.mask
?.getPixel(p.x, p.y)
Expand All @@ -1963,19 +2011,19 @@ export abstract class Filter {
let re = val0 + nSigma * RandomUtils.grand();
let im = val0 + nSigma * RandomUtils.grand();
let val = Math.sqrt(re * re + im * im);
const r = Math.trunc(val);
const r = MathUtils.clamp(Math.trunc(val), 0, p.maxChannelValue);

val0 = p.g / sqrt2;
re = val0 + nSigma * RandomUtils.grand();
im = val0 + nSigma * RandomUtils.grand();
val = Math.sqrt(re * re + im * im);
const g = Math.trunc(val);
const g = MathUtils.clamp(Math.trunc(val), 0, p.maxChannelValue);

val0 = p.b / sqrt2;
re = val0 + nSigma * RandomUtils.grand();
im = val0 + nSigma * RandomUtils.grand();
val = Math.sqrt(re * re + im * im);
const b = Math.trunc(val);
const b = MathUtils.clamp(Math.trunc(val), 0, p.maxChannelValue);

const a = p.a;

Expand Down
134 changes: 131 additions & 3 deletions test/filter/filter.noise.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/** @format */

import { decodePng, encodePng, Filter } from '../../src';
import { decodePng, encodePng, Filter, NoiseType } from '../../src';
import { TestFolder } from '../_utils/test-folder';
import { TestSection } from '../_utils/test-section';
import { TestUtils } from '../_utils/test-utils';

describe('Filter', () => {
test('noise', () => {
test('noise gaussian', () => {
const input = TestUtils.readFromFile(
TestFolder.input,
TestSection.png,
Expand All @@ -32,7 +32,135 @@ describe('Filter', () => {
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'noise.png',
'noise_gaussian.png',
output
);
});

test('noise uniform', () => {
const input = TestUtils.readFromFile(
TestFolder.input,
TestSection.png,
'buck_24.png'
);

const i0 = decodePng({
data: input,
});
expect(i0).toBeDefined();
if (i0 === undefined) {
return;
}

Filter.noise({
image: i0,
sigma: 10,
type: NoiseType.uniform,
});

const output = encodePng({
image: i0,
});
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'noise_uniform.png',
output
);
});

test('noise saltAndPepper', () => {
const input = TestUtils.readFromFile(
TestFolder.input,
TestSection.png,
'buck_24.png'
);

const i0 = decodePng({
data: input,
});
expect(i0).toBeDefined();
if (i0 === undefined) {
return;
}

Filter.noise({
image: i0,
sigma: 10,
type: NoiseType.saltAndPepper,
});

const output = encodePng({
image: i0,
});
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'noise_saltAndPepper.png',
output
);
});

test('noise poisson', () => {
const input = TestUtils.readFromFile(
TestFolder.input,
TestSection.png,
'buck_24.png'
);

const i0 = decodePng({
data: input,
});
expect(i0).toBeDefined();
if (i0 === undefined) {
return;
}

Filter.noise({
image: i0,
sigma: 10,
type: NoiseType.poisson,
});

const output = encodePng({
image: i0,
});
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'noise_poisson.png',
output
);
});

test('noise rice', () => {
const input = TestUtils.readFromFile(
TestFolder.input,
TestSection.png,
'buck_24.png'
);

const i0 = decodePng({
data: input,
});
expect(i0).toBeDefined();
if (i0 === undefined) {
return;
}

Filter.noise({
image: i0,
sigma: 10,
type: NoiseType.rice,
});

const output = encodePng({
image: i0,
});
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'noise_rice.png',
output
);
});
Expand Down

0 comments on commit 62353fe

Please sign in to comment.