Skip to content

Commit

Permalink
feat: add harmonic and geometric mean
Browse files Browse the repository at this point in the history
  • Loading branch information
golota60 committed Apr 20, 2023
1 parent 8b4d397 commit 4e03768
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 7 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ fixImage(png: MinimumData, options: FixOptions)

Below you can find all the existing strategies and what they do underneath the hood. PRs are open for more!

- Strategies.MAJORITY - take the color that occurs the most often in the block
- Strategies.AVERAGE - take the average of colors in the blocks
- Strategies.ALG(05|10|20|30|40|50|60|70|80|90) - a mix; if a color is making up above X%, then take it, otherwise take the average
- `Strategies.MAJORITY` - take the color that occurs the most often in the block
- `Strategies.AVERAGE` - take the average of colors in the blocks
- `Strategies.ALG(05|10|20|30|40|50|60|70|80|90)` - a mix; if a color is making up above X%, then take it, otherwise take the average

### Examples

Expand Down
11 changes: 11 additions & 0 deletions src/fixImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
Pixel,
Strategies,
getAverageOfColors,
getGeometricMeanOfColors,
getHarmonicMeanOfColors,
getMajorityColor,
} from "./utils";

Expand Down Expand Up @@ -84,7 +86,16 @@ export const fixImage = <T extends MinimumData>(

blocks[bI] = new Array(block.length).fill(color);
break;
case Strategies.HARMONIC:
const harmonic = getHarmonicMeanOfColors(block);

blocks[bI] = new Array(block.length).fill(harmonic);
break;
case Strategies.GEOMETRIC:
const geometric = getGeometricMeanOfColors(block);

blocks[bI] = new Array(block.length).fill(geometric);
break;
// get all the alg cases
default:
const coverage: number = occurences / (outPixWidth * outPixHeight);
Expand Down
65 changes: 61 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export const Strategies = Object.freeze({
MAJORITY: "majority",
// just take the mean out of colors in the current block
AVERAGE: "average",
// harmonic mean
HARMONIC: "harmonic",
// geometric mean
GEOMETRIC: "geometric",
// take the color only if it is present for over X% of the image, otherwise take average
ALG05: 0.05,
ALG10: 0.1,
Expand All @@ -41,22 +45,30 @@ export interface FixOptions {
}

const accumulateColors = (pixels: Array<Pixel>, i: number) =>
pixels.map((e) => e[i]);

const sumColors = (pixels: Array<Pixel>, i: number) =>
pixels.reduce((acc, item) => {
return acc + item[i];
}, 0);

const multiplyColors = (pixels: Array<Pixel>, i: number) =>
pixels.reduce((acc, item) => {
return acc * item[i];
}, 1);

export const getAverageOfColors = (pixels: Array<Pixel>): Pixel => {
const redAccumulated = accumulateColors(pixels, 0);
const redAccumulated = sumColors(pixels, 0);
const red = redAccumulated / pixels.length;

const greenAccumulated = accumulateColors(pixels, 1);
const greenAccumulated = sumColors(pixels, 1);
const green = greenAccumulated / pixels.length;

const blueAccumulated = accumulateColors(pixels, 2);
const blueAccumulated = sumColors(pixels, 2);
const blue = blueAccumulated / pixels.length;

// TODO: Rethink whether alpha should be in this
const alphaAccumulated = accumulateColors(pixels, 3);
const alphaAccumulated = sumColors(pixels, 3);
const alpha = alphaAccumulated / pixels.length;

return [
Expand All @@ -67,6 +79,51 @@ export const getAverageOfColors = (pixels: Array<Pixel>): Pixel => {
];
};

const getHarmonicMean = (numsToAvg: Array<number>) => {
const sumOfNegativePowers = numsToAvg
.map((num) => 1 / num)
.reduce((acc, num) => acc + num, 0);
return Math.round(numsToAvg.length / sumOfNegativePowers);
};

export const getHarmonicMeanOfColors = (pixels: Array<Pixel>) => {
const redAccumulated = accumulateColors(pixels, 0);
const red = getHarmonicMean(redAccumulated);

const greenAccumulated = accumulateColors(pixels, 1);
const green = getHarmonicMean(greenAccumulated);

const blueAccumulated = accumulateColors(pixels, 2);
const blue = getHarmonicMean(blueAccumulated);

const alphaAccumulated = accumulateColors(pixels, 3);
const alpha = getHarmonicMean(alphaAccumulated);

return [red, green, blue, alpha];
};

const getGeometricMean = (numsToAvg: Array<number>) => {
const product = numsToAvg.reduce((acc, item) => acc * item, 1);

return Math.round(Math.pow(product, 1 / numsToAvg.length));
};

export const getGeometricMeanOfColors = (pixels: Array<Pixel>) => {
const redAccumulated = accumulateColors(pixels, 0);
const red = getGeometricMean(redAccumulated);

const greenAccumulated = accumulateColors(pixels, 1);
const green = getGeometricMean(greenAccumulated);

const blueAccumulated = accumulateColors(pixels, 2);
const blue = getGeometricMean(blueAccumulated);

const alphaAccumulated = accumulateColors(pixels, 3);
const alpha = getGeometricMean(alphaAccumulated);

return [red, green, blue, alpha];
};

interface MajorityColorData {
color: Pixel;
occurences: number; // n of occurences
Expand Down

0 comments on commit 4e03768

Please sign in to comment.