<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta charset="utf-8" /> <title>smartcrop.js 图像内容感智能裁剪</title> </head> <body> <h2><a href="https://github.com/jwagner/smartcrop.js/">smartcrop.js</a> 内容感知图像裁剪</h2> <div> <form> <div class=drop> <h2>拖拽图片进行分析</h2> <input type=file name=file accept="image/*" /> </div> <label>宽度<div><input name=width type="range" min=50 max=500 step=1 value=250 /><span class=value>250</span>px</div></label> <label>高度<div><input name=height type="range" min=50 max=500 step=1 value=250 /><span class=value>250</span>px</div></label> <label>最小比例<div><input name=minScale type="range" min=0.5 max=1.0 step=0.1 value=1 /><span class=value>1.0</span></div></label> <p><label>三分法<input name=ruleOfThirds type="checkbox" value=1 checked /></label></p> <p><strong>人脸检测</strong></p> <div><label><input name=faceDetection type=radio value=off checked />关</label></div> <div> <label><input name=faceDetection type=radio value=jquery />使用 <a href="http://facedetection.jaysalvat.com/">jquery.facedetection</a></label> </div> <div> <label><input name=faceDetection type=radio value=tracking />使用 <a href="https://trackingjs.com/">tracking.js</a></label> </div> <div> <label class=opencv-loading><input name=faceDetection type=radio value=opencv disabled />使用 <a href="https://opencv.org/">opencv.js</a></label> </div> </form> </div> <div class=output> <canvas id="c"></canvas> </div> <div class=sidebar> <div id=debug></div> <h4>All Crops</h4> <div class=crops> </div> </div> <script src="https://ss.netnr.com/jquery@3.7.1/dist/jquery.min.js"></script> <script src="https://cors.zme.ink/29a.ch/sandbox/2014/smartcrop/examples/underscore.js"></script> <script src="https://cors.zme.ink/29a.ch/sandbox/2014/smartcrop/smartcrop.js"></script> <script src="https://cors.zme.ink/29a.ch/sandbox/2014/smartcrop/examples/smartcrop-debug.js"></script> <script src="https://cors.zme.ink/29a.ch/sandbox/2014/smartcrop/examples/tracking-min.js"></script> <script src="https://cors.zme.ink/29a.ch/sandbox/2014/smartcrop/examples/tracking-face-min.js"></script> <script src="https://cors.zme.ink/29a.ch/sandbox/2014/smartcrop/examples/jquery.facedetection.min.js"></script> <script src="https://ss.netnr.com/opencv.js@1.2.1/opencv.js" async defer onload="openCvReady()"></script> </script> </body> </html>
html { width: 100%; min-height: 100%; } body { font-family: sans-serif; max-width: 1400px; padding: 8px; margin: auto; color: deeppink; } .dark body { max-width: 1240px; } .dark { background: #111; color: #888; } .dark a { color: #8af; } h1 { font-weight: normal; } p { max-width: 900px; } input[type="file"] { display: block; } input[type="range"] { width: 300px; } img, canvas { padding: 8px; box-sizing: border-box; max-width: 100%; margin: auto; } .output { width: 50%; float: left; } .testsuite img { max-width: 40%; } .testsuite > div { margin-top: 2em; } .testsuite > .testsuite-image-title { margin-top: 0; } .sidebar { width: 50%; float: left; height: 600px; } #slide, #prev-slide { max-width: 100%; width: 1280px; height: 720px; overflow: hidden; } #slide { background: #ccc; } #prev-slide { position: absolute; z-index: 1; } #prev-slide img { transition: all 1s linear; -webkit-transition: all 1s linear; opacity: 0.9; max-width: none; padding: 0; margin: 0; } #slide img { transition: all 5s linear; -webkit-transition: all 5s linear; /*transition-delay: 1s;*/ /*-webkit-transition-delay: 1s; [> Safari <]*/ opacity: 1; max-width: none; padding: 0; margin: 0; } .opencv-loading { color: #666; position: relative; } @keyframes spinner { to { transform: rotate(360deg); } } .opencv-loading:after { content: ""; box-sizing: border-box; display: inline-block; width: 1ex; height: 1ex; margin-left: 1em; border-radius: 50%; border: 2px solid #ccc; border-top-color: #333; animation: spinner 0.6s linear infinite; } .drop { background: #eee; padding: 1em; margin-bottom: 2em; }
(function () { var canvas = $('canvas')[0]; var form = document.forms[0]; var ctx = canvas.getContext('2d'); var img; $('html') .on('dragover', function (e) { e.preventDefault(); return false; }) .on('drop', function (e) { var files = e.originalEvent.dataTransfer.files; handleFiles(files); return false; }); $('input[type=file]').change(function () { handleFiles(this.files); }); function handleFiles(files) { if (files.length > 0) { var file = files[0]; if ( typeof FileReader !== 'undefined' && file.type.indexOf('image') != -1 ) { var reader = new FileReader(); // Note: addEventListener doesn't work in Google Chrome for this event reader.onload = function (evt) { load(evt.target.result); }; reader.readAsDataURL(file); } } } load('https://gs.zme.ink/static/wallpaper/02.jpg'); $('input[type=range]').on( 'input', _.debounce(function () { $(this) .next('.value') .text($(this).val()); run(); }, 500) ); $('input[type=radio], input[type=checkbox]').on( 'change', _.debounce(function () { run(); }) ); function load(src) { fetch(src, { method: 'get', responseType: 'blob' }).then(res => { return res.blob(); }).then(blob => { img = new Image(); img.onload = function () { run(); }; img.src = window.URL.createObjectURL(blob); }) } function run() { if (!img) return; var options = { width: form.width.value * 1, height: form.height.value * 1, minScale: form.minScale.value * 1, ruleOfThirds: form.ruleOfThirds.checked, debug: true }; var faceDetection = $('input[name=faceDetection]:checked', form).val(); if (faceDetection === 'tracking') { faceDetectionTracking(options, function () { analyze(options); }); } else if (faceDetection === 'jquery') { faceDetectionJquery(options, function () { analyze(options); }); } else if (faceDetection === 'opencv') { faceDetectionOpenCV(options, function () { analyze(options); }); } else { analyze(options); } } function prescaleImage(image, maxDimension, callback) { // tracking.js is very slow on big images so make sure the image is reasonably small var width = image.naturalWidth || image.width; var height = image.naturalHeight || image.height; if (width < maxDimension && height < maxDimension) return callback(image, 1); var scale = Math.min(maxDimension / width, maxDimension / height); var canvas = document.createElement('canvas'); canvas.width = ~~(width * scale); canvas.height = ~~(height * scale); canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height); var result = document.createElement('img'); result.onload = function () { callback(result, scale); }; result.src = canvas.toDataURL(); } function faceDetectionOpenCV(options, callback) { prescaleImage(img, 768, function (img, scale) { var src = cv.imread(img); var gray = new cv.Mat(); cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0); var faces = new cv.RectVector(); var faceCascade = new cv.CascadeClassifier(); // load pre-trained classifiers faceCascade.load('haarcascade_frontalface_default.xml'); console.log(faceCascade); // detect faces var msize = new cv.Size(0, 0); // let c = document.createElement('canvas'); // cv.imshow(c, gray); // document.body.appendChild(c) faceCascade.detectMultiScale(gray, faces, 1.1, 3, 0, msize, msize); options.boost = []; for (var i = 0; i < faces.size(); ++i) { var face = faces.get(i); options.boost.push({ x: face.x / scale, y: face.y / scale, width: face.width / scale, height: face.height / scale, weight: 1.0 }); } src.delete(); gray.delete(); faceCascade.delete(); faces.delete(); callback(); }); } function faceDetectionTracking(options, callback) { prescaleImage(img, 768, function (img, scale) { var tracker = new tracking.ObjectTracker('face'); tracking.track(img, tracker); tracker.on('track', function (event) { console.log( 'tracking.js detected ' + event.data.length + ' faces', event.data ); options.boost = event.data.map(function (face) { return { x: face.x / scale, y: face.y / scale, width: face.width / scale, height: face.height / scale, weight: 1.0 }; }); callback(); }); }); } function faceDetectionJquery(options, callback) { $(img).faceDetection({ complete: function (faces) { if (faces === false) { return console.log('jquery.facedetection returned false'); } console.log( 'jquery.facedetection detected ' + faces.length + ' faces', faces ); options.boost = Array.prototype.slice .call(faces, 0) .map(function (face) { return { x: face.x, y: face.y, width: face.width, height: face.height, weight: 1.0 }; }); callback(); } }); } function analyze(options) { console.log(options); smartcrop.crop(img, options, draw); } function draw(result) { var selectedCrop = result.topCrop; $('.crops') .empty() .append( _.sortBy(result.crops, function (c) { return -c.score.total; }).map(function (crop) { return $('<p>') .text( 'Score: ' + ~~(crop.score.total * 10000000) + ', ' + crop.x + 'x' + crop.y ) .hover( function () { drawCrop(crop); }, function () { drawCrop(selectedCrop); } ) .click(function () { selectedCrop = crop; drawCrop(selectedCrop); }) .data('crop', crop); }) ); drawCrop(selectedCrop); $('#debug') .empty() .append(debugDraw(result, true)); } function drawCrop(crop) { canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); ctx.strokeStyle = 'red'; ctx.lineWidth = 4; ctx.strokeRect(crop.x, crop.y, crop.width, crop.height); } window.openCvReady = function () { console.log('opencv code ready'); loadCascade( 'haarcascade_frontalface_default.xml', 'https://ss.netnr.com/opencv.js@1.2.1/tests/haarcascade_frontalface_default.xml', function () { console.log('opencv ready'); document.querySelector('.opencv-loading input').disabled = false; document.querySelector('.opencv-loading').className = ''; } ); }; function loadCascade(path, url, callback) { var request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.onload = function () { if (request.readyState === 4) { if (request.status === 200) { var data = new Uint8Array(request.response); cv.FS_createDataFile('/', path, data, true, false, false); callback(); } else { self.printError( 'Failed to load ' + url + ' status: ' + request.status ); } } }; request.send(); } })();