Client-side nudity detection

Developer
Size
6,271 Kb
Views
22,264

How do I make an client-side nudity detection?

Updated implementation of https://www.patrick-wied.at/static/nudejs/. The old one was pretty dirty, actual worker code can be cleaned up.. What is a client-side nudity detection? How do you make a client-side nudity detection? This script and codes were developed by Chad Scira on 13 October 2022, Thursday.

Client-side nudity detection Previews

Client-side nudity detection - Script Codes HTML Codes

<!DOCTYPE html>
<html >
<head> <meta charset="UTF-8"> <title>Client-side nudity detection</title> <link rel="stylesheet" href="css/style.css">
</head>
<body> <h1>Drag & Drop <br /> Client-side image nudity detection</h1> <script src="js/index.js"></script>
</body>
</html>

Client-side nudity detection - Script Codes CSS Codes

body { font-size: 26px; font-family: helvetica; background: #666;
}
h1 { position: absolute; left: 0; right: 0; top: 50%; margin: 0; margin-top: -65px; color: #fff; text-align: center;
}
canvas { display: block; margin: auto;
}

Client-side nudity detection - Script Codes JS Codes

(function () {	function TaskWorker ($config) {	this.worker = this.createWorker();	this.worker.addEventListener('message', this.onWorkerMessage.bind(this));	this.tasks = [];	this.onTaskComplete = $config.onTaskComplete;	this.lastTaskTimestamp = null;	}	TaskWorker.prototype = {	functionToObjectURL: function (func) {	var blob,	stringFunc = func.toString();	stringFunc = stringFunc.substring(stringFunc.indexOf('{') + 1, stringFunc.lastIndexOf('}'))	try {	blob = new Blob([stringFunc], { 'type' : 'text/javascript' });	} catch (error) { // Backwards-compatibility	window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;	blob = new BlobBuilder();	blob.append(stringFunc);	blob = blob.getBlob();	}	return (window.URL || window.webkitURL).createObjectURL(blob);	},	createWorker: function () {	return new Worker(this.functionToObjectURL(function () {	onmessage = function (event) {	var message = event.data;	postMessage({id: message.id, result: eval('(' + message.func + ')').apply(null, message.arguments)});	}	}));	},	generateTaskID: function () {	var id = Math.random(),	exists = false;	this.tasks.some(function (task) {	if (task.id === true) {	exists = true;	return true;	}	});	return exists ? this.generateTaskID() : id;	},	onWorkerMessage: function (event) {	var message = event.data,	taskIndex = null;	this.tasks.some(function (task, index) {	if (message.id === task.id) {	taskIndex = index;	return true;	}	});	if (taskIndex !== null) {	this.tasks[taskIndex].callback(message.result);	this.onTaskComplete(this);	this.tasks.splice(taskIndex, 1);	}	},	run: function ($options) {	var id = this.generateTaskID();	this.lastTaskTimestamp = new Date();	this.tasks.push({	id: id,	callback: $options.callback	})	this.worker.postMessage({	id: id,	arguments: $options.arguments,	func: String($options.function)	});	}	};	function TaskWorkerManager ($config) {	$config = $config || {};	this.maxWorkers = navigator.hardwareConcurrency || $config.maxWorkers || 4;	this.idleTimeout = $config.idleTimeout || 10000;	this.workers = [];	this.queue = [];	this.onWorkerTaskComplete = this.onWorkerTaskComplete.bind(this);	this.flushIdleWorkers = this.flushIdleWorkers.bind(this);	if (this.idleTimeout !== false) {	setInterval(this.flushIdleWorkers, 1000);	}	}	TaskWorkerManager.prototype = {	run: function (task) {	this.queue.push(task);	this.next();	},	next: function () {	if (!this.queue.length) return;	var worker = this.getWorker();	if (!worker) return;	var task = this.queue.shift();	worker.run(task);	},	onWorkerTaskComplete: function () {	this.next();	},	flushIdleWorkers: function () {	this.workers = this.workers.filter(function (worker) {	if (worker.tasks.length === 0 && new Date() - worker.lastTaskTimestamp > this.idleTimeout) {	worker.worker.terminate();	return false;	} else {	return true;	}	}, this);	},	getWorker: function () {	var idleWorkers = this.workers.filter(function (worker) {	return worker.tasks.length === 0;	});	if (idleWorkers.length) {	return idleWorkers[0];	} else if (this.workers.length < this.maxWorkers) {	return this.createWorker();	} else {	return null;	}	},	createWorker: function () {	var worker = new TaskWorker({	onTaskComplete: this.onWorkerTaskComplete	});	this.workers.push(worker);	return worker;	}	};	window.Task = new TaskWorkerManager();
})(window);
function isNude (data, width, height, callback) {	/* * Nude.js - Nudity detection with Javascript and HTMLCanvas * * Author: Patrick Wied ( https://www.patrick-wied.at ) * Version: 0.1 (2010-11-21) * License: MIT License */ Task.run({ arguments: [data, width, height], function: function (data, width, height) {	Array.prototype.remove = function(index) { var rest = this.slice(index + 1); this.length = index; return this.push.apply(this, rest);	};	var skinRegions = [],	skinMap = [],	canvas = {width: width, height: height};	return scanImage(data);	function scanImage(imageData){	var detectedRegions = [],	mergeRegions = [],	width = canvas.width,	lastFrom = -1,	lastTo = -1;	var addMerge = function(from, to){	lastFrom = from;	lastTo = to;	var len = mergeRegions.length,	fromIndex = -1,	toIndex = -1;	while(len--){	var region = mergeRegions[len],	rlen = region.length;	while(rlen--){	if(region[rlen] == from){	fromIndex = len;	}	if(region[rlen] == to){	toIndex = len;	}	}	}	if(fromIndex != -1 && toIndex != -1 && fromIndex == toIndex){	return;	}	if(fromIndex == -1 && toIndex == -1){	mergeRegions.push([from, to]);	return;	}	if(fromIndex != -1 && toIndex == -1){	mergeRegions[fromIndex].push(to);	return;	}	if(fromIndex == -1 && toIndex != -1){	mergeRegions[toIndex].push(from);	return;	}	if(fromIndex != -1 && toIndex != -1 && fromIndex != toIndex){	mergeRegions[fromIndex] = mergeRegions[fromIndex].concat(mergeRegions[toIndex]);	mergeRegions.remove(toIndex);	return;	}	};	// iterate the image from the top left to the bottom right	var length = imageData.length,	width = canvas.width;	for(var i = 0, u = 1; i < length; i+=4, u++){	var r = imageData[i],	g = imageData[i+1],	b = imageData[i+2],	x = (u>width)?((u%width)-1):u,	y = (u>width)?(Math.ceil(u/width)-1):1;	if(classifySkin(r, g, b)){ //	skinMap.push({"id": u, "skin": true, "region": 0, "x": x, "y": y, "checked": false});	var region = -1,	checkIndexes = [u-2, (u-width)-2, u-width-1, (u-width)],	checker = false;	for(var o = 0; o < 4; o++){	var index = checkIndexes[o];	if(skinMap[index] && skinMap[index].skin){	if(skinMap[index].region!=region && region!=-1 && lastFrom!=region && lastTo!=skinMap[index].region){	addMerge(region, skinMap[index].region);	}	region = skinMap[index].region;	checker = true;	}	}	if(!checker){	skinMap[u-1].region = detectedRegions.length;	detectedRegions.push([skinMap[u-1]]);	continue;	}else{	if(region > -1){	if(!detectedRegions[region]){	detectedRegions[region] = [];	}	skinMap[u-1].region = region;	detectedRegions[region].push(skinMap[u-1]);	}	}	}else{	skinMap.push({"id": u, "skin": false, "region": 0, "x": x, "y": y, "checked": false});	}	}	merge(detectedRegions, mergeRegions);	return analyseRegions();	};	// function for merging detected regions	function merge(detectedRegions, mergeRegions){	var length = mergeRegions.length,	detRegions = [];	// merging detected regions	while(length--){	var region = mergeRegions[length],	rlen = region.length;	if(!detRegions[length])	detRegions[length] = [];	while(rlen--){	var index = region[rlen];	detRegions[length] = detRegions[length].concat(detectedRegions[index]);	detectedRegions[index] = [];	}	}	// push the rest of the regions to the detRegions array	// (regions without merging)	var l = detectedRegions.length;	while(l--){	if(detectedRegions[l].length > 0){	detRegions.push(detectedRegions[l]);	}	}	// clean up	clearRegions(detRegions);	};	// clean up function	// only pushes regions which are bigger than a specific amount to the final result	function clearRegions(detectedRegions){	var length = detectedRegions.length;	for(var i=0; i < length; i++){	if(detectedRegions[i].length > 30){	skinRegions.push(detectedRegions[i]);	}	}	};	function analyseRegions(){	// sort the detected regions by size	var length = skinRegions.length,	totalPixels = canvas.width * canvas.height,	totalSkin = 0;	// if there are less than 3 regions	if(length < 3){	// postMessage(false);	// close();	return false;	}	// sort the skinRegions with bubble sort algorithm	(function(){	var sorted = false;	while(!sorted){	sorted = true;	for(var i = 0; i < length-1; i++){	if(skinRegions[i].length < skinRegions[i+1].length){	sorted = false;	var temp = skinRegions[i];	skinRegions[i] = skinRegions[i+1];	skinRegions[i+1] = temp;	}	}	}	})();	// count total skin pixels	while(length--){	totalSkin += skinRegions[length].length;	}	// check if there are more than 15% skin pixel in the image	if((totalSkin/totalPixels)*100 < 15){	// if the percentage lower than 15, it's not nude!	//console.log("it's not nude :) - total skin percent is "+((totalSkin/totalPixels)*100)+"% ");	// postMessage(false);	// close();	return false;	}	// check if the largest skin region is less than 35% of the total skin count	// AND if the second largest region is less than 30% of the total skin count	// AND if the third largest region is less than 30% of the total skin count	if((skinRegions[0].length/totalSkin)*100 < 35	&& (skinRegions[1].length/totalSkin)*100 < 30	&& (skinRegions[2].length/totalSkin)*100 < 30){	// the image is not nude.	//console.log("it's not nude :) - less than 35%,30%,30% skin in the biggest areas :" + ((skinRegions[0].length/totalSkin)*100) + "%, " + ((skinRegions[1].length/totalSkin)*100)+"%, "+((skinRegions[2].length/totalSkin)*100)+"%");	// postMessage(false);	// close();	return false;	}	// check if the number of skin pixels in the largest region is less than 45% of the total skin count	if((skinRegions[0].length/totalSkin)*100 < 45){	// it's not nude	//console.log("it's not nude :) - the biggest region contains less than 45%: "+((skinRegions[0].length/totalSkin)*100)+"%");	// postMessage(false);	// close();	return false;	}	// TODO:	// build the bounding polygon by the regions edge values:	// Identify the leftmost, the uppermost, the rightmost, and the lowermost skin pixels of the three largest skin regions.	// Use these points as the corner points of a bounding polygon.	// TODO:	// check if the total skin count is less than 30% of the total number of pixels	// AND the number of skin pixels within the bounding polygon is less than 55% of the size of the polygon	// if this condition is true, it's not nude.	// TODO: include bounding polygon functionality	// if there are more than 60 skin regions and the average intensity within the polygon is less than 0.25	// the image is not nude	if(skinRegions.length > 60){	//console.log("it's not nude :) - more than 60 skin regions");	// postMessage(false);	// close();	return false;	}	// otherwise it is nude	// postMessage(true);	// close();	return true;	};	function classifySkin(r, g, b){	// A Survey on Pixel-Based Skin Color Detection Techniques	var rgbClassifier = ((r>95) && (g>40 && g <100) && (b>20) && ((Math.max(r,g,b) - Math.min(r,g,b)) > 15) && (Math.abs(r-g)>15) && (r > g) && (r > b)),	nurgb = toNormalizedRgb(r, g, b),	nr = nurgb[0],	ng = nurgb[1],	nb = nurgb[2],	normRgbClassifier = (((nr/ng)>1.185) && (((r*b)/(Math.pow(r+g+b,2))) > 0.107) && (((r*g)/(Math.pow(r+g+b,2))) > 0.112)),	//hsv = toHsv(r, g, b),	//h = hsv[0]*100,	//s = hsv[1],	//hsvClassifier = (h < 50 && h > 0 && s > 0.23 && s < 0.68);	hsv = toHsvTest(r, g, b),	h = hsv[0],	s = hsv[1],	hsvClassifier = (h > 0 && h < 35 && s > 0.23 && s < 0.68);	/* * ycc doesnt work	ycc = toYcc(r, g, b),	y = ycc[0],	cb = ycc[1],	cr = ycc[2],	yccClassifier = ((y > 80) && (cb > 77 && cb < 127) && (cr > 133 && cr < 173));	*/	return (rgbClassifier || normRgbClassifier || hsvClassifier); //	};	function toYcc(r, g, b){	r/=255,g/=255,b/=255;	var y = 0.299*r + 0.587*g + 0.114*b,	cr = r - y,	cb = b - y;	return [y, cr, cb];	};	function toHsv(r, g, b){	return [ // hue Math.acos((0.5*((r-g)+(r-b)))/(Math.sqrt((Math.pow((r-g),2)+((r-b)*(g-b)))))), // saturation 1-(3*((Math.min(r,g,b))/(r+g+b))), // value (1/3)*(r+g+b) ];	};	function toHsvTest(r, g, b){	var h = 0,	mx = Math.max(r, g, b),	mn = Math.min(r, g, b),	dif = mx - mn;	if(mx == r){	h = (g - b)/dif;	}else if(mx == g){	h = 2+((g - r)/dif)	}else{	h = 4+((r - g)/dif);	}	h = h*60;	if(h < 0){	h = h+360;	}	return [h, 1-(3*((Math.min(r,g,b))/(r+g+b))),(1/3)*(r+g+b)] ;	};	function toNormalizedRgb(r, g, b){	var sum = r+g+b;	return [(r/sum), (g/sum), (b/sum)];	}; }, callback: callback });
}
window.addEventListener('dragover', function (evt) {	evt.preventDefault();
}, true);
// Handle dropped image file - only Firefox and Google Chrome
window.addEventListener('drop', function (evt) {	evt.preventDefault();	Array.prototype.slice.call(document.querySelectorAll('canvas')).forEach(function (canvas) {	document.body.removeChild(canvas);	});
if (document.querySelector('h1')) { document.body.removeChild(document.querySelector('h1'));
}	var files = evt.dataTransfer.files;	if (files.length > 0) {	var file = files[0];	if (typeof FileReader !== 'undefined' && file.type.indexOf('image') != -1) {	var reader = new FileReader();	reader.addEventListener('load', function (evt) {	var image = new Image(),	canvas = document.createElement('canvas'),	context = canvas.getContext('2d');	canvas.style.border = '10px dashed grey';	image.addEventListener('load', function () {	var scale = 1 / Math.max(image.width/600, image.height/600);	var scaledWidth	= image.width * scale;	var scaledHeight = image.height * scale;	canvas.width = scaledWidth;	canvas.height = scaledHeight;	context.drawImage(image, 0,0,image.width, image.height, 0,0, scaledWidth, scaledHeight);	document.body.appendChild(canvas);	var imageData = context.getImageData(0, 0, canvas.width, canvas.height);	isNude(imageData.data, imageData.width, imageData.height, function (nude) {	canvas.style.border = '10px solid ' + (nude ? 'red' : 'green');	});	});	image.src = evt.target.result;	});	reader.readAsDataURL(file);	}	}	evt.preventDefault();
}, true);
Client-side nudity detection - Script Codes
Client-side nudity detection - Script Codes
Home Page Home
Developer Chad Scira
Username icodeforlove
Uploaded October 13, 2022
Rating 3
Size 6,271 Kb
Views 22,264
Do you need developer help for Client-side nudity detection?

Find the perfect freelance services for your business! Fiverr's mission is to change how the world works together. Fiverr connects businesses with freelancers offering digital services in 500+ categories. Find Developer!

Chad Scira (icodeforlove) Script Codes
Create amazing SEO content with AI!

Jasper is the AI Content Generator that helps you and your team break through creative blocks to create amazing, original content 10X faster. Discover all the ways the Jasper AI Content Platform can help streamline your creative workflows. Start For Free!