smartcrop-rails 1.1.1 → 2.0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: ''