Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- console.log('foo');
- /** smart-crop.js
- * A javascript library implementing content aware image cropping
- *
- * Copyright (C) 2014 Jonas Wagner
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- (function(){
- "use strict";
- function SmartCrop(options){
- this.options = extend({}, SmartCrop.DEFAULTS, options);
- }
- SmartCrop.DEFAULTS = {
- width: 0,
- height: 0,
- aspect: 0,
- cropWidth: 0,
- cropHeight: 0,
- detailWeight: 0.2,
- skinColor: [0.78, 0.57, 0.44],
- skinBias: 0.01,
- skinBrightnessMin: 0.2,
- skinBrightnessMax: 1.0,
- skinThreshold: 0.8,
- skinWeight: 1.8,
- saturationBrightnessMin: 0.05,
- saturationBrightnessMax: 0.9,
- saturationThreshold: 0.4,
- saturationBias: 0.2,
- saturationWeight: 0.3,
- // step * minscale rounded down to the next power of two should be good
- scoreDownSample: 8,
- step: 8,
- scaleStep: 0.1,
- minScale: 0.9,
- maxScale: 1.0,
- edgeRadius: 0.4,
- edgeWeight: -20.0,
- outsideImportance: -0.5,
- ruleOfThirds: true,
- prescale: true,
- canvasFactory: null,
- debug: false
- };
- SmartCrop.crop = function(image, options, callback){
- if(options.aspect){
- options.width = options.aspect;
- options.height = 1;
- }
- // work around images scaled in css by drawing them onto a canvas
- if(image.naturalWidth && (image.naturalWidth != image.width || image.naturalHeight != image.height)){
- var c = new SmartCrop(options).canvas(image.naturalWidth, image.naturalHeight),
- cctx = c.getContext('2d');
- c.width = image.naturalWidth;
- c.height = image.naturalHeight;
- cctx.drawImage(image, 0, 0);
- image = c;
- }
- var scale = 1,
- prescale = 1;
- if(options.width && options.height) {
- scale = min(image.width/options.width, image.height/options.height);
- options.cropWidth = ~~(options.width * scale);
- options.cropHeight = ~~(options.height * scale);
- // img = 100x100, width = 95x95, scale = 100/95, 1/scale > min
- // don't set minscale smaller than 1/scale
- // -> don't pick crops that need upscaling
- options.minScale = min(options.maxScale || SmartCrop.DEFAULTS.maxScale, max(1/scale, (options.minScale||SmartCrop.DEFAULTS.minScale)));
- }
- var smartCrop = new SmartCrop(options);
- if(options.width && options.height) {
- if(options.prescale !== false){
- prescale = 1/scale/options.minScale;
- if(prescale < 1) {
- var prescaledCanvas = smartCrop.canvas(image.width*prescale, image.height*prescale),
- ctx = prescaledCanvas.getContext('2d');
- ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, prescaledCanvas.width, prescaledCanvas.height);
- image = prescaledCanvas;
- smartCrop.options.cropWidth = ~~(options.cropWidth*prescale);
- smartCrop.options.cropHeight = ~~(options.cropHeight*prescale);
- }
- else {
- prescale = 1;
- }
- }
- }
- var result = smartCrop.analyse(image);
- for(var i = 0, i_len = result.crops.length; i < i_len; i++) {
- var crop = result.crops[i];
- crop.x = ~~(crop.x/prescale);
- crop.y = ~~(crop.y/prescale);
- crop.width = ~~(crop.width/prescale);
- crop.height = ~~(crop.height/prescale);
- }
- callback(result);
- return result;
- };
- // check if all the dependencies are there
- SmartCrop.isAvailable = function(options){
- try {
- var s = new this(options),
- c = s.canvas(16, 16);
- return typeof c.getContext === 'function';
- }
- catch(e){
- return false;
- }
- };
- SmartCrop.prototype = {
- canvas: function(w, h){
- if(this.options.canvasFactory !== null){
- return this.options.canvasFactory(w, h);
- }
- var c = document.createElement('canvas');
- c.width = w;
- c.height = h;
- return c;
- },
- edgeDetect: function(i, o){
- var id = i.data,
- od = o.data,
- w = i.width,
- h = i.height;
- for(var y = 0; y < h; y++) {
- for(var x = 0; x < w; x++) {
- var p = (y*w+x)*4,
- lightness;
- if(x === 0 || x >= w-1 || y === 0 || y >= h-1){
- lightness = sample(id, p);
- }
- else {
- lightness = sample(id, p)*4 - sample(id, p-w*4) - sample(id, p-4) - sample(id, p+4) - sample(id, p+w*4);
- }
- od[p+1] = lightness;
- }
- }
- },
- skinDetect: function(i, o){
- var id = i.data,
- od = o.data,
- w = i.width,
- h = i.height,
- options = this.options;
- for(var y = 0; y < h; y++) {
- for(var x = 0; x < w; x++) {
- var p = (y*w+x)*4,
- lightness = cie(id[p], id[p+1], id[p+2])/255,
- skin = this.skinColor(id[p], id[p+1], id[p+2]);
- if(skin > options.skinThreshold && lightness >= options.skinBrightnessMin && lightness <= options.skinBrightnessMax){
- od[p] = (skin-options.skinThreshold)*(255/(1-options.skinThreshold));
- }
- else {
- od[p] = 0;
- }
- }
- }
- },
- saturationDetect: function(i, o){
- var id = i.data,
- od = o.data,
- w = i.width,
- h = i.height,
- options = this.options;
- for(var y = 0; y < h; y++) {
- for(var x = 0; x < w; x++) {
- var p = (y*w+x)*4,
- lightness = cie(id[p], id[p+1], id[p+2])/255,
- sat = saturation(id[p], id[p+1], id[p+2]);
- if(sat > options.saturationThreshold && lightness >= options.saturationBrightnessMin && lightness <= options.saturationBrightnessMax){
- od[p+2] = (sat-options.saturationThreshold)*(255/(1-options.saturationThreshold));
- }
- else {
- od[p+2] = 0;
- }
- }
- }
- },
- crops: function(image){
- var crops = [],
- width = image.width,
- height = image.height,
- options = this.options,
- minDimension = min(width, height),
- cropWidth = options.cropWidth || minDimension,
- cropHeight = options.cropHeight || minDimension;
- for(var scale = options.maxScale; scale >= options.minScale; scale -= options.scaleStep){
- for(var y = 0; y+cropHeight*scale <= height; y+=options.step) {
- for(var x = 0; x+cropWidth*scale <= width; x+=options.step) {
- crops.push({
- x: x,
- y: y,
- width: cropWidth*scale,
- height: cropHeight*scale
- });
- }
- }
- }
- return crops;
- },
- score: function(output, crop){
- var score = {
- detail: 0,
- saturation: 0,
- skin: 0,
- total: 0
- },
- options = this.options,
- od = output.data,
- downSample = options.scoreDownSample,
- invDownSample = 1/downSample,
- outputHeightDownSample = output.height*downSample,
- outputWidthDownSample = output.width*downSample,
- outputWidth = output.width;
- for(var y = 0; y < outputHeightDownSample; y+=downSample) {
- for(var x = 0; x < outputWidthDownSample; x+=downSample) {
- var p = (~~(y*invDownSample)*outputWidth+~~(x*invDownSample))*4,
- importance = this.importance(crop, x, y),
- detail = od[p+1]/255;
- score.skin += od[p]/255*(detail+options.skinBias)*importance;
- score.detail += detail*importance;
- score.saturation += od[p+2]/255*(detail+options.saturationBias)*importance;
- }
- }
- score.total = (score.detail*options.detailWeight + score.skin*options.skinWeight + score.saturation*options.saturationWeight)/crop.width/crop.height;
- return score;
- },
- importance: function(crop, x, y){
- var options = this.options;
- if (crop.x > x || x >= crop.x+crop.width || crop.y > y || y >= crop.y+crop.height) return options.outsideImportance;
- x = (x-crop.x)/crop.width;
- y = (y-crop.y)/crop.height;
- var px = abs(0.5-x)*2,
- py = abs(0.5-y)*2,
- // distance from edge
- dx = Math.max(px-1.0+options.edgeRadius, 0),
- dy = Math.max(py-1.0+options.edgeRadius, 0),
- d = (dx*dx+dy*dy)*options.edgeWeight;
- var s = 1.41-sqrt(px*px+py*py);
- if(options.ruleOfThirds){
- s += (Math.max(0, s+d+0.5)*1.2)*(thirds(px)+thirds(py));
- }
- return s+d;
- },
- skinColor: function(r, g, b){
- var mag = sqrt(r*r+g*g+b*b),
- options = this.options,
- rd = (r/mag-options.skinColor[0]),
- gd = (g/mag-options.skinColor[1]),
- bd = (b/mag-options.skinColor[2]),
- d = sqrt(rd*rd+gd*gd+bd*bd);
- return 1-d;
- },
- analyse: function(image){
- var result = {},
- options = this.options,
- canvas = this.canvas(image.width, image.height),
- ctx = canvas.getContext('2d');
- ctx.drawImage(image, 0, 0);
- var input = ctx.getImageData(0, 0, canvas.width, canvas.height),
- output = ctx.getImageData(0, 0, canvas.width, canvas.height);
- this.edgeDetect(input, output);
- this.skinDetect(input, output);
- this.saturationDetect(input, output);
- var scoreCanvas = this.canvas(ceil(image.width/options.scoreDownSample), ceil(image.height/options.scoreDownSample)),
- scoreCtx = scoreCanvas.getContext('2d');
- ctx.putImageData(output, 0, 0);
- scoreCtx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, scoreCanvas.width, scoreCanvas.height);
- var scoreOutput = scoreCtx.getImageData(0, 0, scoreCanvas.width, scoreCanvas.height);
- var topScore = -Infinity,
- topCrop = null,
- crops = this.crops(image);
- for(var i = 0, i_len = crops.length; i < i_len; i++) {
- var crop = crops[i];
- crop.score = this.score(scoreOutput, crop);
- if(crop.score.total > topScore){
- topCrop = crop;
- topScore = crop.score.total;
- }
- }
- result.crops = crops;
- result.topCrop = topCrop;
- if(options.debug && topCrop){
- ctx.fillStyle = 'rgba(255, 0, 0, 0.1)';
- ctx.fillRect(topCrop.x, topCrop.y, topCrop.width, topCrop.height);
- for (var y = 0; y < output.height; y++) {
- for (var x = 0; x < output.width; x++) {
- var p = (y * output.width + x) * 4;
- var importance = this.importance(topCrop, x, y);
- if (importance > 0) {
- output.data[p + 1] += importance * 32;
- }
- if (importance < 0) {
- output.data[p] += importance * -64;
- }
- output.data[p + 3] = 255;
- }
- }
- ctx.putImageData(output, 0, 0);
- ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';
- ctx.strokeRect(topCrop.x, topCrop.y, topCrop.width, topCrop.height);
- result.debugCanvas = canvas;
- }
- return result;
- }
- };
- // aliases and helpers
- var min = Math.min,
- max = Math.max,
- abs = Math.abs,
- ceil = Math.ceil,
- sqrt = Math.sqrt;
- function extend(o){
- for(var i = 1, i_len = arguments.length; i < i_len; i++) {
- var arg = arguments[i];
- if(arg){
- for(var name in arg){
- o[name] = arg[name];
- }
- }
- }
- return o;
- }
- // gets value in the range of [0, 1] where 0 is the center of the pictures
- // returns weight of rule of thirds [0, 1]
- function thirds(x){
- x = ((x-(1/3)+1.0)%2.0*0.5-0.5)*16;
- return Math.max(1.0-x*x, 0.0);
- }
- function cie(r, g, b){
- return 0.5126*b + 0.7152*g + 0.0722*r;
- }
- function sample(id, p) {
- return cie(id[p], id[p+1], id[p+2]);
- }
- function saturation(r, g, b){
- var maximum = max(r/255, g/255, b/255), minumum = min(r/255, g/255, b/255);
- if(maximum === minumum){
- return 0;
- }
- var l = (maximum + minumum) / 2,
- d = maximum-minumum;
- return l > 0.5 ? d/(2-maximum-minumum) : d/(maximum+minumum);
- }
- // amd
- if (typeof define !== 'undefined' && define.amd) define(function(){return SmartCrop;});
- //common js
- if (typeof exports !== 'undefined') exports.SmartCrop = SmartCrop;
- // browser
- else if (typeof navigator !== 'undefined') window.SmartCrop = SmartCrop;
- // nodejs
- if (typeof module !== 'undefined') {
- module.exports = SmartCrop;
- }
- })();
- /**
- * @licence GNU GPL v2+
- * @author Jonas Kress
- */
- (function( wb, mw, $, SmartCrop ) {
- 'use strict';
- /**
- * Offers access to the page image
- *
- * Uses pageimages and pageprops Api endpoints to retrieve the image URL
- *
- * @constructor
- *
- * @param {int} width
- * @param {int} height
- */
- var SELF = wb.PageImage = function PageImage( width, height ) {
- if ( width ) {
- this._width = width;
- }
- if ( height ) {
- this._height = height;
- }
- };
- SELF.prototype._width = 200;
- SELF.prototype._height = 200;
- SELF.prototype._thumbnailSize = 900;
- SELF.prototype._minScale = 1;
- SELF.prototype._crossOriginXmlUrl = 'https://upload.wikimedia.org/crossdomain.xml';
- /**
- * Returns the page image as DOM element
- * @return {Object} jQuery.Promise Resolved after loading and cropping of image is done
- * returning a DOM element.
- */
- SELF.prototype.getPageImage = function() {
- var deferred = $.Deferred(),
- self = this;
- self._getImageUrl().done( function( url, referenceUrl ){
- self._loadImage( url ).done(
- function( image ) {
- self._getSmartCrop( image ).done(function( crop ) {
- deferred.resolve(self._getMaskedImage( image, crop, referenceUrl ));
- });
- });
- } );
- return deferred.promise();
- };
- /**
- * @private
- **/
- SELF.prototype._loadImage = function( url ) {
- var deferred = $.Deferred();
- var image = new Image();
- image.onload = function() {
- deferred.resolve( image );
- };
- image.crossOrigin = this._crossOriginXmlUrl;
- image.src = url;
- return deferred.promise();
- };
- /**
- * @private
- **/
- SELF.prototype._getSmartCrop = function( image ) {
- var deferred = $.Deferred();
- SmartCrop.crop( image, {
- width : this._width,
- height : this._height,
- minScale: this._minScale,
- }, function( result ) {
- deferred.resolve( result.topCrop );
- });
- return deferred.promise();
- };
- /**
- * @private
- **/
- SELF.prototype._getMaskedImage = function( image, crop, ref ) {
- var canvas = $('<canvas/>')[0], ctx = canvas.getContext('2d');
- if( ref ){
- $(canvas).data( 'ref', ref );
- }
- canvas.width = this._width;
- canvas.height = this._height;
- ctx.drawImage( image, crop.x, crop.y, crop.width, crop.height, 0, 0,
- canvas.width, canvas.height );
- return canvas;
- };
- /**
- * @private
- **/
- SELF.prototype._getImageUrl = function() {
- var deferred = $.Deferred(),
- self = this;
- mw.loader.using( 'mediawiki.api', function() {
- ( new mw.Api()).get( {
- action : 'query',
- prop : 'pageimages|pageprops',
- piprop: 'thumbnail',
- pithumbsize: self._thumbnailSize,
- titles: mw.config.get( 'wgPageName' )
- } ).done( function( data ) {
- var page = data.query.pages[Object.keys( data.query.pages )[0]];
- if( page.thumbnail ){
- deferred.resolve( page.thumbnail.source,
- page.pageprops.page_image );
- }
- });
- });
- return deferred.promise();
- };
- }(wikibase, mw, jQuery, SmartCrop));
- var pageImage = new wikibase.PageImage(365, 365);
- var div = $('<div/>');
- $('.wikibase-entityview-side').prepend(div);
- pageImage.getPageImage().done(function( element ){
- div.append(element) ;
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement