smartcrop-rails 1.1.1 → 2.0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: fc9c87f222c553066dbdb65dd4245e35b29b6962
4
- data.tar.gz: 635ecfae7ad3c53d8e6724a7c11675deeef1ddec
2
+ SHA256:
3
+ metadata.gz: 96a12d057d28f18a7d36e002ad00d78c7aff0c578979db0634d7dd9c4e6e82ef
4
+ data.tar.gz: 5614f5178fb58dcbfd9fc4c276e84fcf06fa5e817b70744a8fa9b3c9b6d13ee9
5
5
  SHA512:
6
- metadata.gz: 4d521f826a551d83b11cfe9421d593254dfbcf270b47f56169cf317740502a28e558998819614601e5983534ddbe8a6f97c75ac43cb5bb2a5d2f0978a5a28f48
7
- data.tar.gz: 9b029c80277a075f2d3aba0a9a1b19c59267bfc4fcba7f9de6e33aefe241ac790ec442c8fb750093fbca870310b51de26772fd7852fd3459ce7ddb8997a43d21
6
+ metadata.gz: bbf0b4e638ba75c31f42e8c4402390956417d12ac8b205a6b329a26254d3bb0fd949a9c17568669907f10c99c151d1f4efc6cefa1e71d37d20637fb5e0e050e2
7
+ data.tar.gz: 8bd56e65876bdaacf18fc4c211bf97971a344f50231aa112b1f04005864f6fe6ffb012da3a8eecc373c2e68b21214fd424aa6e776df9dcd08a286fed09593291
@@ -1,5 +1,5 @@
1
1
  module Smartcrop
2
2
  module Rails
3
- VERSION = "1.1.1"
3
+ VERSION = "2.0.3.1"
4
4
  end
5
5
  end
@@ -2,7 +2,7 @@
2
2
  * smartcrop.js
3
3
  * A javascript library implementing content aware image cropping
4
4
  *
5
- * Copyright (C) 2016 Jonas Wagner
5
+ * Copyright (C) 2018 Jonas Wagner
6
6
  *
7
7
  * Permission is hereby granted, free of charge, to any person obtaining
8
8
  * a copy of this software and associated documentation files (the
@@ -25,526 +25,571 @@
25
25
  */
26
26
 
27
27
  (function() {
28
- 'use strict';
29
-
30
- var smartcrop = {};
31
- // Promise implementation to use
32
- smartcrop.Promise = typeof Promise !== 'undefined' ? Promise : function() {
33
- throw new Error('No native promises and smartcrop.Promise not set.');
34
- };
35
-
36
- smartcrop.DEFAULTS = {
37
- width: 0,
38
- height: 0,
39
- aspect: 0,
40
- cropWidth: 0,
41
- cropHeight: 0,
42
- detailWeight: 0.2,
43
- skinColor: [0.78, 0.57, 0.44],
44
- skinBias: 0.01,
45
- skinBrightnessMin: 0.2,
46
- skinBrightnessMax: 1.0,
47
- skinThreshold: 0.8,
48
- skinWeight: 1.8,
49
- saturationBrightnessMin: 0.05,
50
- saturationBrightnessMax: 0.9,
51
- saturationThreshold: 0.4,
52
- saturationBias: 0.2,
53
- saturationWeight: 0.3,
54
- // Step * minscale rounded down to the next power of two should be good
55
- scoreDownSample: 8,
56
- step: 8,
57
- scaleStep: 0.1,
58
- minScale: 1.0,
59
- maxScale: 1.0,
60
- edgeRadius: 0.4,
61
- edgeWeight: -20.0,
62
- outsideImportance: -0.5,
63
- boostWeight: 100.0,
64
- ruleOfThirds: true,
65
- prescale: true,
66
- imageOperations: null,
67
- canvasFactory: defaultCanvasFactory,
68
- // Factory: defaultFactories,
69
- debug: false,
70
- };
71
-
72
-
73
-
74
- smartcrop.crop = function(inputImage, options_, callback) {
75
- var options = extend({}, smartcrop.DEFAULTS, options_);
76
-
77
- if (options.aspect) {
78
- options.width = options.aspect;
79
- options.height = 1;
80
- }
28
+ 'use strict';
29
+
30
+ var smartcrop = {};
31
+ // Promise implementation to use
32
+ smartcrop.Promise =
33
+ typeof Promise !== 'undefined'
34
+ ? Promise
35
+ : function() {
36
+ throw new Error('No native promises and smartcrop.Promise not set.');
37
+ };
38
+
39
+ smartcrop.DEFAULTS = {
40
+ width: 0,
41
+ height: 0,
42
+ aspect: 0,
43
+ cropWidth: 0,
44
+ cropHeight: 0,
45
+ detailWeight: 0.2,
46
+ skinColor: [0.78, 0.57, 0.44],
47
+ skinBias: 0.01,
48
+ skinBrightnessMin: 0.2,
49
+ skinBrightnessMax: 1.0,
50
+ skinThreshold: 0.8,
51
+ skinWeight: 1.8,
52
+ saturationBrightnessMin: 0.05,
53
+ saturationBrightnessMax: 0.9,
54
+ saturationThreshold: 0.4,
55
+ saturationBias: 0.2,
56
+ saturationWeight: 0.1,
57
+ // Step * minscale rounded down to the next power of two should be good
58
+ scoreDownSample: 8,
59
+ step: 8,
60
+ scaleStep: 0.1,
61
+ minScale: 1.0,
62
+ maxScale: 1.0,
63
+ edgeRadius: 0.4,
64
+ edgeWeight: -20.0,
65
+ outsideImportance: -0.5,
66
+ boostWeight: 100.0,
67
+ ruleOfThirds: true,
68
+ prescale: true,
69
+ imageOperations: null,
70
+ canvasFactory: defaultCanvasFactory,
71
+ // Factory: defaultFactories,
72
+ debug: false
73
+ };
81
74
 
82
- if (options.imageOperations === null) {
83
- options.imageOperations = canvasImageOperations(options.canvasFactory);
84
- }
75
+ smartcrop.crop = function(inputImage, options_, callback) {
76
+ var options = extend({}, smartcrop.DEFAULTS, options_);
85
77
 
86
- var iop = options.imageOperations;
87
-
88
- var scale = 1;
89
- var prescale = 1;
90
-
91
- return iop.open(inputImage, options.input).then(function(image) {
92
-
93
- if (options.width && options.height) {
94
- scale = min(image.width / options.width, image.height / options.height);
95
- options.cropWidth = ~~(options.width * scale);
96
- options.cropHeight = ~~(options.height * scale);
97
- // Img = 100x100, width = 95x95, scale = 100/95, 1/scale > min
98
- // don't set minscale smaller than 1/scale
99
- // -> don't pick crops that need upscaling
100
- options.minScale = min(options.maxScale, max(1 / scale, options.minScale));
101
-
102
- if (options.prescale !== false) {
103
- prescale = 1 / scale / options.minScale;
104
- if (prescale < 1) {
105
- image = iop.resample(image, image.width * prescale, image.height * prescale);
106
- options.cropWidth = ~~(options.cropWidth * prescale);
107
- options.cropHeight = ~~(options.cropHeight * prescale);
108
- if (options.boost) {
109
- options.boost = options.boost.map(function(boost) {
110
- return {
111
- x: ~~(boost.x * prescale),
112
- y: ~~(boost.y * prescale),
113
- width: ~~(boost.width * prescale),
114
- height: ~~(boost.height * prescale),
115
- weight: boost.weight
116
- };
117
- });
118
- }
119
- }
120
- else {
121
- prescale = 1;
122
- }
123
- }
78
+ if (options.aspect) {
79
+ options.width = options.aspect;
80
+ options.height = 1;
124
81
  }
125
- return image;
126
- })
127
- .then(function(image) {
128
- return iop.getData(image).then(function(data) {
129
- var result = analyse(options, data);
130
-
131
- var crops = result.crops || [result.topCrop];
132
- for (var i = 0, iLen = crops.length; i < iLen; i++) {
133
- var crop = crops[i];
134
- crop.x = ~~(crop.x / prescale);
135
- crop.y = ~~(crop.y / prescale);
136
- crop.width = ~~(crop.width / prescale);
137
- crop.height = ~~(crop.height / prescale);
138
- }
139
- if (callback) callback(result);
140
- return result;
141
- });
142
- });
143
- };
144
-
145
-
146
- // Check if all the dependencies are there
147
- // todo:
148
- smartcrop.isAvailable = function(options) {
149
- if (!smartcrop.Promise) return false;
150
82
 
151
- var canvasFactory = options ? options.canvasFactory : defaultCanvasFactory;
152
-
153
- if (canvasFactory === defaultCanvasFactory) {
154
- var c = document.createElement('canvas');
155
- if (!c.getContext('2d')) {
156
- return false;
83
+ if (options.imageOperations === null) {
84
+ options.imageOperations = canvasImageOperations(options.canvasFactory);
157
85
  }
158
- }
159
86
 
160
- return true;
161
- };
87
+ var iop = options.imageOperations;
88
+
89
+ var scale = 1;
90
+ var prescale = 1;
91
+
92
+ // open the image
93
+ return iop
94
+ .open(inputImage, options.input)
95
+ .then(function(image) {
96
+ // calculate desired crop dimensions based on the image size
97
+ if (options.width && options.height) {
98
+ scale = min(
99
+ image.width / options.width,
100
+ image.height / options.height
101
+ );
102
+ options.cropWidth = ~~(options.width * scale);
103
+ options.cropHeight = ~~(options.height * scale);
104
+ // Img = 100x100, width = 95x95, scale = 100/95, 1/scale > min
105
+ // don't set minscale smaller than 1/scale
106
+ // -> don't pick crops that need upscaling
107
+ options.minScale = min(
108
+ options.maxScale,
109
+ max(1 / scale, options.minScale)
110
+ );
111
+
112
+ // prescale if possible
113
+ if (options.prescale !== false) {
114
+ prescale = min(max(256 / image.width, 256 / image.height), 1);
115
+ if (prescale < 1) {
116
+ image = iop.resample(
117
+ image,
118
+ image.width * prescale,
119
+ image.height * prescale
120
+ );
121
+ options.cropWidth = ~~(options.cropWidth * prescale);
122
+ options.cropHeight = ~~(options.cropHeight * prescale);
123
+ if (options.boost) {
124
+ options.boost = options.boost.map(function(boost) {
125
+ return {
126
+ x: ~~(boost.x * prescale),
127
+ y: ~~(boost.y * prescale),
128
+ width: ~~(boost.width * prescale),
129
+ height: ~~(boost.height * prescale),
130
+ weight: boost.weight
131
+ };
132
+ });
133
+ }
134
+ } else {
135
+ prescale = 1;
136
+ }
137
+ }
138
+ }
139
+ return image;
140
+ })
141
+ .then(function(image) {
142
+ return iop.getData(image).then(function(data) {
143
+ var result = analyse(options, data);
144
+
145
+ var crops = result.crops || [result.topCrop];
146
+ for (var i = 0, iLen = crops.length; i < iLen; i++) {
147
+ var crop = crops[i];
148
+ crop.x = ~~(crop.x / prescale);
149
+ crop.y = ~~(crop.y / prescale);
150
+ crop.width = ~~(crop.width / prescale);
151
+ crop.height = ~~(crop.height / prescale);
152
+ }
153
+ if (callback) callback(result);
154
+ return result;
155
+ });
156
+ });
157
+ };
162
158
 
163
- function edgeDetect(i, o) {
164
- var id = i.data;
165
- var od = o.data;
166
- var w = i.width;
167
- var h = i.height;
159
+ // Check if all the dependencies are there
160
+ // todo:
161
+ smartcrop.isAvailable = function(options) {
162
+ if (!smartcrop.Promise) return false;
168
163
 
169
- for (var y = 0; y < h; y++) {
170
- for (var x = 0; x < w; x++) {
171
- var p = (y * w + x) * 4;
172
- var lightness;
164
+ var canvasFactory = options ? options.canvasFactory : defaultCanvasFactory;
173
165
 
174
- if (x === 0 || x >= w - 1 || y === 0 || y >= h - 1) {
175
- lightness = sample(id, p);
166
+ if (canvasFactory === defaultCanvasFactory) {
167
+ var c = document.createElement('canvas');
168
+ if (!c.getContext('2d')) {
169
+ return false;
176
170
  }
177
- else {
178
- lightness = sample(id, p) * 4 -
171
+ }
172
+
173
+ return true;
174
+ };
175
+
176
+ function edgeDetect(i, o) {
177
+ var id = i.data;
178
+ var od = o.data;
179
+ var w = i.width;
180
+ var h = i.height;
181
+
182
+ for (var y = 0; y < h; y++) {
183
+ for (var x = 0; x < w; x++) {
184
+ var p = (y * w + x) * 4;
185
+ var lightness;
186
+
187
+ if (x === 0 || x >= w - 1 || y === 0 || y >= h - 1) {
188
+ lightness = sample(id, p);
189
+ } else {
190
+ lightness =
191
+ sample(id, p) * 4 -
179
192
  sample(id, p - w * 4) -
180
193
  sample(id, p - 4) -
181
194
  sample(id, p + 4) -
182
195
  sample(id, p + w * 4);
183
- }
196
+ }
184
197
 
185
- od[p + 1] = lightness;
198
+ od[p + 1] = lightness;
199
+ }
186
200
  }
187
201
  }
188
- }
189
-
190
- function skinDetect(options, i, o) {
191
- var id = i.data;
192
- var od = o.data;
193
- var w = i.width;
194
- var h = i.height;
195
-
196
- for (var y = 0; y < h; y++) {
197
- for (var x = 0; x < w; x++) {
198
- var p = (y * w + x) * 4;
199
- var lightness = cie(id[p], id[p + 1], id[p + 2]) / 255;
200
- var skin = skinColor(options, id[p], id[p + 1], id[p + 2]);
201
- var isSkinColor = skin > options.skinThreshold;
202
- var isSkinBrightness = lightness >= options.skinBrightnessMin && lightness <= options.skinBrightnessMax;
203
- if (isSkinColor && isSkinBrightness) {
204
- od[p] = (skin - options.skinThreshold) * (255 / (1 - options.skinThreshold));
205
- }
206
- else {
207
- od[p] = 0;
202
+
203
+ function skinDetect(options, i, o) {
204
+ var id = i.data;
205
+ var od = o.data;
206
+ var w = i.width;
207
+ var h = i.height;
208
+
209
+ for (var y = 0; y < h; y++) {
210
+ for (var x = 0; x < w; x++) {
211
+ var p = (y * w + x) * 4;
212
+ var lightness = cie(id[p], id[p + 1], id[p + 2]) / 255;
213
+ var skin = skinColor(options, id[p], id[p + 1], id[p + 2]);
214
+ var isSkinColor = skin > options.skinThreshold;
215
+ var isSkinBrightness =
216
+ lightness >= options.skinBrightnessMin &&
217
+ lightness <= options.skinBrightnessMax;
218
+ if (isSkinColor && isSkinBrightness) {
219
+ od[p] =
220
+ (skin - options.skinThreshold) *
221
+ (255 / (1 - options.skinThreshold));
222
+ } else {
223
+ od[p] = 0;
224
+ }
208
225
  }
209
226
  }
210
227
  }
211
- }
212
-
213
- function saturationDetect(options, i, o) {
214
- var id = i.data;
215
- var od = o.data;
216
- var w = i.width;
217
- var h = i.height;
218
- for (var y = 0; y < h; y++) {
219
- for (var x = 0; x < w; x++) {
220
- var p = (y * w + x) * 4;
221
-
222
- var lightness = cie(id[p], id[p + 1], id[p + 2]) / 255;
223
- var sat = saturation(id[p], id[p + 1], id[p + 2]);
224
-
225
- var acceptableSaturation = sat > options.saturationThreshold;
226
- var acceptableLightness = lightness >= options.saturationBrightnessMin &&
228
+
229
+ function saturationDetect(options, i, o) {
230
+ var id = i.data;
231
+ var od = o.data;
232
+ var w = i.width;
233
+ var h = i.height;
234
+ for (var y = 0; y < h; y++) {
235
+ for (var x = 0; x < w; x++) {
236
+ var p = (y * w + x) * 4;
237
+
238
+ var lightness = cie(id[p], id[p + 1], id[p + 2]) / 255;
239
+ var sat = saturation(id[p], id[p + 1], id[p + 2]);
240
+
241
+ var acceptableSaturation = sat > options.saturationThreshold;
242
+ var acceptableLightness =
243
+ lightness >= options.saturationBrightnessMin &&
227
244
  lightness <= options.saturationBrightnessMax;
228
- if (acceptableLightness && acceptableLightness) {
229
- od[p + 2] = (sat - options.saturationThreshold) * (255 / (1 - options.saturationThreshold));
230
- }
231
- else {
232
- od[p + 2] = 0;
245
+ if (acceptableLightness && acceptableSaturation) {
246
+ od[p + 2] =
247
+ (sat - options.saturationThreshold) *
248
+ (255 / (1 - options.saturationThreshold));
249
+ } else {
250
+ od[p + 2] = 0;
251
+ }
233
252
  }
234
253
  }
235
254
  }
236
- }
237
255
 
238
- function applyBoosts(options, output) {
239
- if (!options.boost) return;
240
- var od = output.data;
241
- for (var i = 0; i < output.width; i += 4) {
242
- od[i + 3] = 0;
243
- }
244
- for (i = 0; i < options.boost.length; i++) {
245
- applyBoost(options.boost[i], options, output);
246
- }
247
- }
248
-
249
- function applyBoost(boost, options, output) {
250
- var od = output.data;
251
- var w = output.width;
252
- var x0 = ~~boost.x;
253
- var x1 = ~~(boost.x + boost.width);
254
- var y0 = ~~boost.y;
255
- var y1 = ~~(boost.y + boost.height);
256
- var weight = boost.weight * 255;
257
- for (var y = y0; y < y1; y++) {
258
- for (var x = x0; x < x1; x++) {
259
- var i = (y * w + x) * 4;
260
- od[i + 3] += weight;
256
+ function applyBoosts(options, output) {
257
+ if (!options.boost) return;
258
+ var od = output.data;
259
+ for (var i = 0; i < output.width; i += 4) {
260
+ od[i + 3] = 0;
261
+ }
262
+ for (i = 0; i < options.boost.length; i++) {
263
+ applyBoost(options.boost[i], options, output);
261
264
  }
262
265
  }
263
- }
264
-
265
- function generateCrops(options, width, height) {
266
- var results = [];
267
- var minDimension = min(width, height);
268
- var cropWidth = options.cropWidth || minDimension;
269
- var cropHeight = options.cropHeight || minDimension;
270
- for (var scale = options.maxScale; scale >= options.minScale; scale -= options.scaleStep) {
271
- for (var y = 0; y + cropHeight * scale <= height; y += options.step) {
272
- for (var x = 0; x + cropWidth * scale <= width; x += options.step) {
273
- results.push({
274
- x: x,
275
- y: y,
276
- width: cropWidth * scale,
277
- height: cropHeight * scale,
278
- });
266
+
267
+ function applyBoost(boost, options, output) {
268
+ var od = output.data;
269
+ var w = output.width;
270
+ var x0 = ~~boost.x;
271
+ var x1 = ~~(boost.x + boost.width);
272
+ var y0 = ~~boost.y;
273
+ var y1 = ~~(boost.y + boost.height);
274
+ var weight = boost.weight * 255;
275
+ for (var y = y0; y < y1; y++) {
276
+ for (var x = x0; x < x1; x++) {
277
+ var i = (y * w + x) * 4;
278
+ od[i + 3] += weight;
279
279
  }
280
280
  }
281
281
  }
282
- return results;
283
- }
284
-
285
- function score(options, output, crop) {
286
- var result = {
287
- detail: 0,
288
- saturation: 0,
289
- skin: 0,
290
- boost: 0,
291
- total: 0,
292
- };
293
282
 
294
- var od = output.data;
295
- var downSample = options.scoreDownSample;
296
- var invDownSample = 1 / downSample;
297
- var outputHeightDownSample = output.height * downSample;
298
- var outputWidthDownSample = output.width * downSample;
299
- var outputWidth = output.width;
300
-
301
- for (var y = 0; y < outputHeightDownSample; y += downSample) {
302
- for (var x = 0; x < outputWidthDownSample; x += downSample) {
303
- var p = (~~(y * invDownSample) * outputWidth + ~~(x * invDownSample)) * 4;
304
- var i = importance(options, crop, x, y);
305
- var detail = od[p + 1] / 255;
306
-
307
- result.skin += od[p] / 255 * (detail + options.skinBias) * i;
308
- result.detail += detail * i;
309
- result.saturation += od[p + 2] / 255 * (detail + options.saturationBias) * i;
310
- result.boost += od[p + 3] / 255 * i;
283
+ function generateCrops(options, width, height) {
284
+ var results = [];
285
+ var minDimension = min(width, height);
286
+ var cropWidth = options.cropWidth || minDimension;
287
+ var cropHeight = options.cropHeight || minDimension;
288
+ for (
289
+ var scale = options.maxScale;
290
+ scale >= options.minScale;
291
+ scale -= options.scaleStep
292
+ ) {
293
+ for (var y = 0; y + cropHeight * scale <= height; y += options.step) {
294
+ for (var x = 0; x + cropWidth * scale <= width; x += options.step) {
295
+ results.push({
296
+ x: x,
297
+ y: y,
298
+ width: cropWidth * scale,
299
+ height: cropHeight * scale
300
+ });
301
+ }
302
+ }
311
303
  }
304
+ return results;
312
305
  }
313
306
 
314
- result.total = (result.detail * options.detailWeight +
315
- result.skin * options.skinWeight +
316
- result.saturation * options.saturationWeight +
317
- result.boost * options.boostWeight) / (crop.width * crop.height);
318
- return result;
319
- }
307
+ function score(options, output, crop) {
308
+ var result = {
309
+ detail: 0,
310
+ saturation: 0,
311
+ skin: 0,
312
+ boost: 0,
313
+ total: 0
314
+ };
315
+
316
+ var od = output.data;
317
+ var downSample = options.scoreDownSample;
318
+ var invDownSample = 1 / downSample;
319
+ var outputHeightDownSample = output.height * downSample;
320
+ var outputWidthDownSample = output.width * downSample;
321
+ var outputWidth = output.width;
322
+
323
+ for (var y = 0; y < outputHeightDownSample; y += downSample) {
324
+ for (var x = 0; x < outputWidthDownSample; x += downSample) {
325
+ var p =
326
+ (~~(y * invDownSample) * outputWidth + ~~(x * invDownSample)) * 4;
327
+ var i = importance(options, crop, x, y);
328
+ var detail = od[p + 1] / 255;
329
+
330
+ result.skin += od[p] / 255 * (detail + options.skinBias) * i;
331
+ result.detail += detail * i;
332
+ result.saturation +=
333
+ od[p + 2] / 255 * (detail + options.saturationBias) * i;
334
+ result.boost += od[p + 3] / 255 * i;
335
+ }
336
+ }
320
337
 
321
- function importance(options, crop, x, y) {
322
- if (crop.x > x || x >= crop.x + crop.width || crop.y > y || y >= crop.y + crop.height) {
323
- return options.outsideImportance;
324
- }
325
- x = (x - crop.x) / crop.width;
326
- y = (y - crop.y) / crop.height;
327
- var px = abs(0.5 - x) * 2;
328
- var py = abs(0.5 - y) * 2;
329
- // Distance from edge
330
- var dx = Math.max(px - 1.0 + options.edgeRadius, 0);
331
- var dy = Math.max(py - 1.0 + options.edgeRadius, 0);
332
- var d = (dx * dx + dy * dy) * options.edgeWeight;
333
- var s = 1.41 - sqrt(px * px + py * py);
334
- if (options.ruleOfThirds) {
335
- s += (Math.max(0, s + d + 0.5) * 1.2) * (thirds(px) + thirds(py));
338
+ result.total =
339
+ (result.detail * options.detailWeight +
340
+ result.skin * options.skinWeight +
341
+ result.saturation * options.saturationWeight +
342
+ result.boost * options.boostWeight) /
343
+ (crop.width * crop.height);
344
+ return result;
336
345
  }
337
- return s + d;
338
- }
339
- smartcrop.importance = importance;
340
-
341
- function skinColor(options, r, g, b) {
342
- var mag = sqrt(r * r + g * g + b * b);
343
- var rd = (r / mag - options.skinColor[0]);
344
- var gd = (g / mag - options.skinColor[1]);
345
- var bd = (b / mag - options.skinColor[2]);
346
- var d = sqrt(rd * rd + gd * gd + bd * bd);
347
- return 1 - d;
348
- }
349
-
350
- function analyse(options, input) {
351
- var result = {};
352
- var output = new ImgData(input.width, input.height);
353
-
354
- edgeDetect(input, output);
355
- skinDetect(options, input, output);
356
- saturationDetect(options, input, output);
357
- applyBoosts(options, output);
358
-
359
- var scoreOutput = downSample(output, options.scoreDownSample);
360
-
361
- var topScore = -Infinity;
362
- var topCrop = null;
363
- var crops = generateCrops(options, input.width, input.height);
364
-
365
- for (var i = 0, iLen = crops.length; i < iLen; i++) {
366
- var crop = crops[i];
367
- crop.score = score(options, scoreOutput, crop);
368
- if (crop.score.total > topScore) {
369
- topCrop = crop;
370
- topScore = crop.score.total;
371
- }
372
346
 
347
+ function importance(options, crop, x, y) {
348
+ if (
349
+ crop.x > x ||
350
+ x >= crop.x + crop.width ||
351
+ crop.y > y ||
352
+ y >= crop.y + crop.height
353
+ ) {
354
+ return options.outsideImportance;
355
+ }
356
+ x = (x - crop.x) / crop.width;
357
+ y = (y - crop.y) / crop.height;
358
+ var px = abs(0.5 - x) * 2;
359
+ var py = abs(0.5 - y) * 2;
360
+ // Distance from edge
361
+ var dx = Math.max(px - 1.0 + options.edgeRadius, 0);
362
+ var dy = Math.max(py - 1.0 + options.edgeRadius, 0);
363
+ var d = (dx * dx + dy * dy) * options.edgeWeight;
364
+ var s = 1.41 - sqrt(px * px + py * py);
365
+ if (options.ruleOfThirds) {
366
+ s += Math.max(0, s + d + 0.5) * 1.2 * (thirds(px) + thirds(py));
367
+ }
368
+ return s + d;
369
+ }
370
+ smartcrop.importance = importance;
371
+
372
+ function skinColor(options, r, g, b) {
373
+ var mag = sqrt(r * r + g * g + b * b);
374
+ var rd = r / mag - options.skinColor[0];
375
+ var gd = g / mag - options.skinColor[1];
376
+ var bd = b / mag - options.skinColor[2];
377
+ var d = sqrt(rd * rd + gd * gd + bd * bd);
378
+ return 1 - d;
373
379
  }
374
380
 
375
- result.topCrop = topCrop;
381
+ function analyse(options, input) {
382
+ var result = {};
383
+ var output = new ImgData(input.width, input.height);
376
384
 
377
- if (options.debug && topCrop) {
378
- result.crops = crops;
379
- result.debugOutput = output;
380
- result.debugOptions = options;
381
- // Create a copy which will not be adjusted by the post scaling of smartcrop.crop
382
- result.debugTopCrop = extend({}, result.topCrop);
383
- }
384
- return result;
385
- }
386
-
387
- function ImgData(width, height, data) {
388
- this.width = width;
389
- this.height = height;
390
- if (data) {
391
- this.data = new Uint8ClampedArray(data);
385
+ edgeDetect(input, output);
386
+ skinDetect(options, input, output);
387
+ saturationDetect(options, input, output);
388
+ applyBoosts(options, output);
389
+
390
+ var scoreOutput = downSample(output, options.scoreDownSample);
391
+
392
+ var topScore = -Infinity;
393
+ var topCrop = null;
394
+ var crops = generateCrops(options, input.width, input.height);
395
+
396
+ for (var i = 0, iLen = crops.length; i < iLen; i++) {
397
+ var crop = crops[i];
398
+ crop.score = score(options, scoreOutput, crop);
399
+ if (crop.score.total > topScore) {
400
+ topCrop = crop;
401
+ topScore = crop.score.total;
402
+ }
403
+ }
404
+
405
+ result.topCrop = topCrop;
406
+
407
+ if (options.debug && topCrop) {
408
+ result.crops = crops;
409
+ result.debugOutput = output;
410
+ result.debugOptions = options;
411
+ // Create a copy which will not be adjusted by the post scaling of smartcrop.crop
412
+ result.debugTopCrop = extend({}, result.topCrop);
413
+ }
414
+ return result;
392
415
  }
393
- else {
394
- this.data = new Uint8ClampedArray(width * height * 4);
416
+
417
+ function ImgData(width, height, data) {
418
+ this.width = width;
419
+ this.height = height;
420
+ if (data) {
421
+ this.data = new Uint8ClampedArray(data);
422
+ } else {
423
+ this.data = new Uint8ClampedArray(width * height * 4);
424
+ }
395
425
  }
396
- }
397
- smartcrop.ImgData = ImgData;
398
-
399
- function downSample(input, factor) {
400
- var idata = input.data;
401
- var iwidth = input.width;
402
- var width = Math.floor(input.width / factor);
403
- var height = Math.floor(input.height / factor);
404
- var output = new ImgData(width, height);
405
- var data = output.data;
406
- var ifactor2 = 1 / (factor * factor);
407
- for (var y = 0; y < height; y++) {
408
- for (var x = 0; x < width; x++) {
409
- var i = (y * width + x) * 4;
410
-
411
- var r = 0;
412
- var g = 0;
413
- var b = 0;
414
- var a = 0;
415
-
416
- var mr = 0;
417
- var mg = 0;
418
- var mb = 0;
419
-
420
- for (var v = 0; v < factor; v++) {
421
- for (var u = 0; u < factor; u++) {
422
- var j = ((y * factor + v) * iwidth + (x * factor + u)) * 4;
423
- r += idata[j];
424
- g += idata[j + 1];
425
- b += idata[j + 2];
426
- a += idata[j + 3];
427
- mr = Math.max(mr, idata[j]);
428
- mg = Math.max(mg, idata[j + 1]);
429
- mb = Math.max(mb, idata[j + 2]);
426
+ smartcrop.ImgData = ImgData;
427
+
428
+ function downSample(input, factor) {
429
+ var idata = input.data;
430
+ var iwidth = input.width;
431
+ var width = Math.floor(input.width / factor);
432
+ var height = Math.floor(input.height / factor);
433
+ var output = new ImgData(width, height);
434
+ var data = output.data;
435
+ var ifactor2 = 1 / (factor * factor);
436
+ for (var y = 0; y < height; y++) {
437
+ for (var x = 0; x < width; x++) {
438
+ var i = (y * width + x) * 4;
439
+
440
+ var r = 0;
441
+ var g = 0;
442
+ var b = 0;
443
+ var a = 0;
444
+
445
+ var mr = 0;
446
+ var mg = 0;
447
+
448
+ for (var v = 0; v < factor; v++) {
449
+ for (var u = 0; u < factor; u++) {
450
+ var j = ((y * factor + v) * iwidth + (x * factor + u)) * 4;
451
+ r += idata[j];
452
+ g += idata[j + 1];
453
+ b += idata[j + 2];
454
+ a += idata[j + 3];
455
+ mr = Math.max(mr, idata[j]);
456
+ mg = Math.max(mg, idata[j + 1]);
457
+ // unused
458
+ // mb = Math.max(mb, idata[j + 2]);
459
+ }
430
460
  }
461
+ // this is some funky magic to preserve detail a bit more for
462
+ // skin (r) and detail (g). Saturation (b) does not get this boost.
463
+ data[i] = r * ifactor2 * 0.5 + mr * 0.5;
464
+ data[i + 1] = g * ifactor2 * 0.7 + mg * 0.3;
465
+ data[i + 2] = b * ifactor2;
466
+ data[i + 3] = a * ifactor2;
431
467
  }
432
- // this is some funky magic to preserve detail a bit more for
433
- // skin (r) and detail (g). Saturation (b) does not get this boost.
434
- data[i] = r * ifactor2 * 0.5 + mr * 0.5;
435
- data[i + 1] = g * ifactor2 * 0.7 + mg * 0.3;
436
- data[i + 2] = b * ifactor2;
437
- data[i + 3] = a * ifactor2;
438
468
  }
469
+ return output;
439
470
  }
440
- return output;
441
- }
442
- smartcrop._downSample = downSample;
443
-
444
- function defaultCanvasFactory(w, h) {
445
- var c = document.createElement('canvas');
446
- c.width = w;
447
- c.height = h;
448
- return c;
449
- }
450
-
451
- function canvasImageOperations(canvasFactory) {
452
- return {
453
- // Takes imageInput as argument
454
- // returns an object which has at least
455
- // {width: n, height: n}
456
- open: function(image) {
457
- // Work around images scaled in css by drawing them onto a canvas
458
- var w = image.naturalWidth || image.width;
459
- var h = image.naturalHeight || image.height;
460
- var c = canvasFactory(w, h);
461
- var ctx = c.getContext('2d');
462
- if (image.naturalWidth && (image.naturalWidth != image.width || image.naturalHeight != image.height)) {
463
- c.width = image.naturalWidth;
464
- c.height = image.naturalHeight;
465
- }
466
- else {
467
- c.width = image.width;
468
- c.height = image.height;
469
- }
470
- ctx.drawImage(image, 0, 0);
471
- return smartcrop.Promise.resolve(c);
472
- },
473
- // Takes an image (as returned by open), and changes it's size by resampling
474
- resample: function(image, width, height) {
475
- return Promise.resolve(image).then(function(image) {
476
- var c = canvasFactory(~~width, ~~height);
477
- var ctx = c.getContext('2d');
471
+ smartcrop._downSample = downSample;
478
472
 
479
- ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, c.width, c.height);
480
- return smartcrop.Promise.resolve(c);
481
- });
482
- },
483
- getData: function(image) {
484
- return Promise.resolve(image).then(function(c) {
473
+ function defaultCanvasFactory(w, h) {
474
+ var c = document.createElement('canvas');
475
+ c.width = w;
476
+ c.height = h;
477
+ return c;
478
+ }
479
+
480
+ function canvasImageOperations(canvasFactory) {
481
+ return {
482
+ // Takes imageInput as argument
483
+ // returns an object which has at least
484
+ // {width: n, height: n}
485
+ open: function(image) {
486
+ // Work around images scaled in css by drawing them onto a canvas
487
+ var w = image.naturalWidth || image.width;
488
+ var h = image.naturalHeight || image.height;
489
+ var c = canvasFactory(w, h);
485
490
  var ctx = c.getContext('2d');
486
- var id = ctx.getImageData(0, 0, c.width, c.height);
487
- return new ImgData(c.width, c.height, id.data);
488
- });
489
- },
490
- };
491
- }
492
- smartcrop._canvasImageOperations = canvasImageOperations;
493
-
494
- // Aliases and helpers
495
- var min = Math.min;
496
- var max = Math.max;
497
- var abs = Math.abs;
498
- var ceil = Math.ceil;
499
- var sqrt = Math.sqrt;
500
-
501
- function extend(o) {
502
- for (var i = 1, iLen = arguments.length; i < iLen; i++) {
503
- var arg = arguments[i];
504
- if (arg) {
505
- for (var name in arg) {
506
- o[name] = arg[name];
491
+ if (
492
+ image.naturalWidth &&
493
+ (image.naturalWidth != image.width ||
494
+ image.naturalHeight != image.height)
495
+ ) {
496
+ c.width = image.naturalWidth;
497
+ c.height = image.naturalHeight;
498
+ } else {
499
+ c.width = image.width;
500
+ c.height = image.height;
501
+ }
502
+ ctx.drawImage(image, 0, 0);
503
+ return smartcrop.Promise.resolve(c);
504
+ },
505
+ // Takes an image (as returned by open), and changes it's size by resampling
506
+ resample: function(image, width, height) {
507
+ return Promise.resolve(image).then(function(image) {
508
+ var c = canvasFactory(~~width, ~~height);
509
+ var ctx = c.getContext('2d');
510
+
511
+ ctx.drawImage(
512
+ image,
513
+ 0,
514
+ 0,
515
+ image.width,
516
+ image.height,
517
+ 0,
518
+ 0,
519
+ c.width,
520
+ c.height
521
+ );
522
+ return smartcrop.Promise.resolve(c);
523
+ });
524
+ },
525
+ getData: function(image) {
526
+ return Promise.resolve(image).then(function(c) {
527
+ var ctx = c.getContext('2d');
528
+ var id = ctx.getImageData(0, 0, c.width, c.height);
529
+ return new ImgData(c.width, c.height, id.data);
530
+ });
531
+ }
532
+ };
533
+ }
534
+ smartcrop._canvasImageOperations = canvasImageOperations;
535
+
536
+ // Aliases and helpers
537
+ var min = Math.min;
538
+ var max = Math.max;
539
+ var abs = Math.abs;
540
+ var sqrt = Math.sqrt;
541
+
542
+ function extend(o) {
543
+ for (var i = 1, iLen = arguments.length; i < iLen; i++) {
544
+ var arg = arguments[i];
545
+ if (arg) {
546
+ for (var name in arg) {
547
+ o[name] = arg[name];
548
+ }
507
549
  }
508
550
  }
551
+ return o;
509
552
  }
510
- return o;
511
- }
512
-
513
- // Gets value in the range of [0, 1] where 0 is the center of the pictures
514
- // returns weight of rule of thirds [0, 1]
515
- function thirds(x) {
516
- x = ((x - (1 / 3) + 1.0) % 2.0 * 0.5 - 0.5) * 16;
517
- return Math.max(1.0 - x * x, 0.0);
518
- }
519
-
520
- function cie(r, g, b) {
521
- return 0.5126 * b + 0.7152 * g + 0.0722 * r;
522
- }
523
- function sample(id, p) {
524
- return cie(id[p], id[p + 1], id[p + 2]);
525
- }
526
- function saturation(r, g, b) {
527
- var maximum = max(r / 255, g / 255, b / 255);
528
- var minumum = min(r / 255, g / 255, b / 255);
529
-
530
- if (maximum === minumum) {
531
- return 0;
553
+
554
+ // Gets value in the range of [0, 1] where 0 is the center of the pictures
555
+ // returns weight of rule of thirds [0, 1]
556
+ function thirds(x) {
557
+ x = (((x - 1 / 3 + 1.0) % 2.0) * 0.5 - 0.5) * 16;
558
+ return Math.max(1.0 - x * x, 0.0);
559
+ }
560
+
561
+ function cie(r, g, b) {
562
+ return 0.5126 * b + 0.7152 * g + 0.0722 * r;
532
563
  }
564
+ function sample(id, p) {
565
+ return cie(id[p], id[p + 1], id[p + 2]);
566
+ }
567
+ function saturation(r, g, b) {
568
+ var maximum = max(r / 255, g / 255, b / 255);
569
+ var minumum = min(r / 255, g / 255, b / 255);
570
+
571
+ if (maximum === minumum) {
572
+ return 0;
573
+ }
533
574
 
534
- var l = (maximum + minumum) / 2;
535
- var d = maximum - minumum;
536
-
537
- return l > 0.5 ? d / (2 - maximum - minumum) : d / (maximum + minumum);
538
- }
539
-
540
- // Amd
541
- if (typeof define !== 'undefined' && define.amd) define(function() {return smartcrop;});
542
- // Common js
543
- if (typeof exports !== 'undefined') exports.smartcrop = smartcrop;
544
- // Browser
545
- else if (typeof navigator !== 'undefined') window.SmartCrop = window.smartcrop = smartcrop;
546
- // Nodejs
547
- if (typeof module !== 'undefined') {
548
- module.exports = smartcrop;
549
- }
550
- })();
575
+ var l = (maximum + minumum) / 2;
576
+ var d = maximum - minumum;
577
+
578
+ return l > 0.5 ? d / (2 - maximum - minumum) : d / (maximum + minumum);
579
+ }
580
+
581
+ // Amd
582
+ if (typeof define !== 'undefined' && define.amd)
583
+ define(function() {
584
+ return smartcrop;
585
+ });
586
+ // Common js
587
+ if (typeof exports !== 'undefined') exports.smartcrop = smartcrop;
588
+ else if (typeof navigator !== 'undefined')
589
+ // Browser
590
+ window.SmartCrop = window.smartcrop = smartcrop;
591
+ // Nodejs
592
+ if (typeof module !== 'undefined') {
593
+ module.exports = smartcrop;
594
+ }
595
+ })();
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smartcrop-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 2.0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohammed Sadiq
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-02 00:00:00.000000000 Z
11
+ date: 2018-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
80
  version: '0'
81
81
  requirements: []
82
82
  rubyforge_project:
83
- rubygems_version: 2.6.13
83
+ rubygems_version: 2.7.6
84
84
  signing_key:
85
85
  specification_version: 4
86
86
  summary: ''