Skip to content

Instantly share code, notes, and snippets.

@EduardoRFS
Last active March 21, 2020 20:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EduardoRFS/3e4d1a074a843dbb60a3e09c8caf6b4c to your computer and use it in GitHub Desktop.
Save EduardoRFS/3e4d1a074a843dbb60a3e09c8caf6b4c to your computer and use it in GitHub Desktop.
import sharp from 'sharp';
import fs from 'fs-extra';
import _ from 'lodash/fp';
const parseImage = (path: string) =>
Promise.all([
sharp(path).metadata(),
sharp(path)
.grayscale()
.raw()
.toBuffer()
]);
const isBlack = (data: number[]) => data.every(a => a === 0);
const isWhite = (data: number[]) => data.every(a => a >= 240);
const splitImages = (width: number, height: number, raw: Buffer) => {
const lines = _.chunk(width, raw);
const columns = _.zipAll(lines) as number[][];
const points = columns.reduce(
(points, column, i) => (isWhite(column) ? points.concat(i) : points),
[]
);
const columnRanges = (_.zip(points.slice(0, -1), points.slice(1)) as [
number,
number
][]).filter(([a, b]) => a + 1 !== b);
return columnRanges.map(([a, b]) => {
const width = b - a - 1;
const imageColumns = columns.slice(a + 1, b);
const imageLines = _.zipAll(imageColumns) as number[][];
return { lines: imageLines, columns: imageColumns, width, height };
});
};
const findBlackLines = (lines: number[][]) =>
lines.map((line, i) => (isBlack(line) ? i : null)).filter(i => i !== null);
(async () => {
const [{ width, height }, raw] = await parseImage('bolo-3414.png');
if (!width || !height) {
throw new Error('wat missing width or height');
}
const images = splitImages(width, height, raw);
await Promise.all(
images.map((image, i) =>
sharp(Buffer.from(image.lines.flat()), {
raw: { width: image.width, height: image.height, channels: 1 }
}).toFile(`${i}.png`)
)
);
const filteredImages = images.map(image => {
const blackLines = findBlackLines(image.lines);
const linesWithoutBlackLine = image.lines.map((line, lineIndex) => {
if (!blackLines.includes(lineIndex)) {
return line;
}
const aboveLine = image.lines[lineIndex - 1];
const belowLine = image.lines[lineIndex + 1];
// TODO: should split from left to right
// every color in this line is black
return line.map((color, columnIndex) => {
// const abovePixels = aboveLine.slice(
// Math.max(columnIndex - 1 - 1, 0),
// Math.min(columnIndex + 1, image.width)
// );
// const belowPixels = belowLine.slice(
// Math.max(columnIndex - 1 - 1, 0),
// Math.min(columnIndex + 1, image.width)
// );
// const pixels = abovePixels.concat(belowPixels);
const pixels = [aboveLine[columnIndex], belowLine[columnIndex]];
return _.sum(pixels) / pixels.length;
});
});
return {
...image,
lines: linesWithoutBlackLine,
columns: _.zipAll(linesWithoutBlackLine) as number[][]
};
});
const center = (data: number[][]) => {
const nonWhite = data
.map(element => !isWhite(element))
.map((isWhite, index) => (isWhite ? index : null))
.filter(index => typeof index === 'number') as number[];
const firstColumn = nonWhite[0];
const lastColumn = _.last(nonWhite) as number;
return [firstColumn, lastColumn];
};
const centeredImages = filteredImages.map(image => {
const [firstColumn, lastColumn] = center(image.columns);
const [firstLine, lastLine] = center(image.lines);
const newLines = image.lines
.slice(firstLine, lastLine)
.map(line => line.slice(firstColumn, lastColumn));
const newColumns = _.zipAll(newLines) as number[][];
const width = lastColumn - firstColumn;
const height = lastLine - firstLine;
return { width, height, lines: newLines, columns: newColumns };
});
await Promise.all(
centeredImages.map((image, i) =>
sharp(Buffer.from(image.lines.flat()), {
raw: { width: image.width, height: image.height, channels: 1 }
}).toFile(`${i}-centered.png`)
)
);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment