@nsfwspy/node
Version:
A nudity/pornography image classifier built for Node.js.
84 lines (67 loc) • 2.6 kB
text/typescript
import * as tf from '@tensorflow/tfjs-node';
import * as tfjs from '@tensorflow/tfjs';
import * as fs from 'fs';
tf.enableProdMode();
export class NsfwSpy {
private imageSize: number;
private modelPath: string;
private model: tf.GraphModel | null;
constructor(modelPath: string) {
this.imageSize = 224;
this.modelPath = modelPath;
this.model = null;
}
async load(loadOptions?: tfjs.io.LoadOptions) {
this.model = await tf.loadGraphModel(this.modelPath, loadOptions);
}
async classifyImageFromByteArray(imageBuffer: Buffer) {
const outputs = tf.tidy(() => {
if (!this.model) throw new Error("The NsfwSpy model has not been loaded yet.");
const decodedImage = tf.node.decodeImage(imageBuffer, 3)
.toFloat()
.div(tf.scalar(255)) as tf.Tensor3D;
const resizedImage = tf.image.resizeBilinear(decodedImage, [this.imageSize, this.imageSize], true);
const image = resizedImage.reshape([1, this.imageSize, this.imageSize, 3]);
return this.model.execute(
{ 'import/input': image },
['Score']
) as tf.Tensor2D;
});
var data = await outputs.data();
outputs.dispose();
return new NsfwSpyResult(data);
}
async classifyImageFile(filePath: string) {
const imageBuffer = await fs.readFileSync(filePath);
return this.classifyImageFromByteArray(imageBuffer);
}
}
export class NsfwSpyResult {
hentai: number;
neutral: number;
pornography: number;
sexy: number;
predictedLabel: ClassificationTypes;
constructor(results: Uint8Array | Float32Array | Int32Array) {
this.hentai = results[0];
this.neutral = results[1];
this.pornography = results[2];
this.sexy = results[3];
this.predictedLabel = this.toDictionary()[0].key;
}
get isNsfw() {
return this.neutral < 0.5;
}
toDictionary() {
const dictionary: { key: ClassificationTypes, value: number }[] = [
{ key: "hentai", value: this.hentai },
{ key: "neutral", value: this.neutral },
{ key: "pornography", value: this.pornography },
{ key: "sexy", value: this.sexy }
];
return dictionary.sort((a, b) => {
return b.value - a.value;
});
}
}
export type ClassificationTypes = "hentai" | "neutral" | "pornography" | "sexy";