Last active
March 21, 2020 20:23
-
-
Save EduardoRFS/3e4d1a074a843dbb60a3e09c8caf6b4c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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