emoji-net
Version:
EmojiNet is an image to emoji recognizer based on MobileNet / Google Emoji Scavenger Hunt
198 lines (168 loc) • 4.84 kB
text/typescript
import crypto from 'crypto'
import fs from 'fs'
import path from 'path'
import {
createCanvas as _createCanvas,
createImageData as _createImageData,
loadImage as _loadImage,
Canvas,
} from 'canvas'
// const _createCanvas = require('canvas').createCanvas
// const _createImageData = require('canvas').createImageData
// const _loadImage = require('canvas').loadImage
function imageMd5 (image: ImageData | HTMLImageElement): string {
if ((image as any).src) { // HTMLImageElement
image = imageToData(image as HTMLImageElement)
} else {
image = image as ImageData
}
const buffer = Buffer.from(image.data.buffer as ArrayBuffer)
return crypto.createHash('md5')
.update(buffer)
.digest('hex')
}
function imageToData (image: HTMLImageElement): ImageData {
const canvas = createCanvas(image.width, image.height)
const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('getContext found null')
}
ctx.drawImage(image, 0, 0, image.width, image.height)
const imageData = ctx.getImageData(0, 0, image.width, image.height)
return imageData
}
async function dataToImage (data: ImageData): Promise<HTMLImageElement> {
const canvas = createCanvas(data.width, data.height)
const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('getContext found null')
}
ctx.putImageData(data, 0, 0)
const dataUrl = canvas.toDataURL()
const image = await loadImage(dataUrl)
return image
}
function cropImage (
imageData: ImageData,
x: number,
y: number,
width: number,
height: number,
): ImageData {
const canvas = createCanvas(imageData.width, imageData.height)
const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('getContext found null')
}
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
// Issues #12 negative x/y value bug
ctx.putImageData(imageData, 0, 0)
const croppedImageData = ctx.getImageData(x, y, width, height)
return croppedImageData
}
async function resizeImage (
image: ImageData | HTMLImageElement,
width: number,
height: number,
): Promise<ImageData> {
if ((image as any).data) { // ImageData
image = await dataToImage(image as ImageData)
} else {
image = image as HTMLImageElement
}
const canvas = createCanvas(width, height)
const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('getContext found null')
}
ctx.drawImage(image, 0, 0, width, height)
const resizedImage = ctx.getImageData(0, 0, width, height)
return resizedImage
}
async function loadImage (src: string | Buffer): Promise<HTMLImageElement> {
const image = await _loadImage(src)
if (!image) {
throw new Error('no image for src:' + src)
}
return image as any as HTMLImageElement
}
function createCanvas (
width: number,
height: number,
): HTMLCanvasElement {
const canvas = _createCanvas(width, height)
return canvas as any as HTMLCanvasElement
}
async function saveImage (
imageData: ImageData,
filename: string,
): Promise<void> {
const ext = path.extname(filename) as '.png' | '.jpg'
const canvas = createCanvas(imageData.width, imageData.height)
const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('no ctx')
}
ctx.putImageData(imageData, 0, 0)
let stream: NodeJS.ReadableStream
switch (ext) {
case '.jpg':
stream = (canvas as any as Canvas).createJPEGStream()
break
case '.png':
stream = (canvas as any as Canvas).createPNGStream()
break
default:
throw new Error('unsupported type: ' + ext)
}
const outFile = fs.createWriteStream(filename)
stream.pipe(outFile)
return new Promise<void>((resolve, reject) => {
outFile.on('close', resolve)
outFile.on('error', reject)
stream.on('error', reject)
})
}
function createImageData (
array: Uint8ClampedArray,
width: number,
height: number,
): ImageData {
return _createImageData(array, width, height)
}
function toDataURL (
data: ImageData,
): string {
const canvas = createCanvas(data.width, data.height)
const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('getContext found null')
}
ctx.putImageData(data, 0, 0)
return canvas.toDataURL()
}
function toBuffer (
data: ImageData,
): Buffer {
const canvas = createCanvas(data.width, data.height)
const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('getContext found null')
}
ctx.putImageData(data, 0, 0)
// https://github.com/Automattic/node-canvas#canvastobuffer
return (canvas as any as Canvas).toBuffer()
}
export {
saveImage,
createCanvas,
createImageData,
cropImage,
dataToImage,
imageMd5,
imageToData,
loadImage,
resizeImage,
toDataURL,
toBuffer,
}