Skip to content

Commit afc15fd

Browse files
committed
Added facedetection support/examples
1 parent 513ddf0 commit afc15fd

24 files changed

+357
-77
lines changed

.editorconfig

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[*.js]
2+
indent_style = space
3+
indent_size = 2

.jscsrc

+1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"maximumLineLength": 120,
44
"requireCurlyBraces": null,
55
"requireCapitalizedComments": null,
6+
"requireTrailingComma": null,
67
"disallowKeywordsOnNewLine": null
78
}

CONTRIBUTING.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# How to contribute
2+
3+
## Reporting Bugs
4+
5+
When reporting a bug please provide **all** the information needed to quickly reproduce the problem.
6+
7+
This includes:
8+
- The browser & version used
9+
- A URL to reproduce the error
10+
- Other steps other necessary to reproduce the error
11+
12+
If I can't reproduce your problem it will likely not get fixed.
13+
14+
## Contributing Code
15+
16+
### Before Hacking
17+
To avoid wasting your time please communicate before starting to hack
18+
on a new feature / change.
19+
Creating an issue describing your plans is a good start.
20+
21+
### Before submitting a pull request
22+
- Try to make your code look like the code around it.
23+
- Provide tests where appropriate.
24+
- Run JSHint & JSCS on the files you changed, fix any issues related to your changes.
25+
- Run the tests, make sure they are green
26+
27+
### Please Avoid
28+
- Reformat my code to fit your personal preferences.
29+
30+
### License
31+
By contributing your code, you agree to license your contribution under the MIT License.
32+
33+
## Setting up the development environment
34+
Before you can start working you'll need to have node.js and git installed.
35+
```
36+
$ git clone
37+
$ cd normalmap.js
38+
$ npm install -g grunt jshint jscs
39+
$ npm install
40+
$ grunt
41+
```
42+
I work on GNU/Linux. Things might not work on windows.
43+
44+
## Running the tests
45+
The tests for smartcrop are fairly simplistic at the moment.
46+
47+
You can run them by opening the [/test/](http://localhost:8000/test/)
48+
folder after running `grunt`.
49+
50+
The tests show the actual output next to the expected output and a diff.
51+
52+
## Code of Conduct
53+
None. Just be who you are.

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2014 Jonas Wagner
1+
Copyright (c) 2016 Jonas Wagner
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy
44
of this software and associated documentation files (the "Software"), to deal

README.md

+49-16
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# smartcrop.js
22

33
Smartcrop.js implements an algorithm to find good crops for images.
4+
It can be used in the browser, in node or via a CLI.
45

56
![Example](http://29a.ch/sandbox/2014/smartcrop/example.jpg)
67
Image: [https://www.flickr.com/photos/endogamia/5682480447/](https://www.flickr.com/photos/endogamia/5682480447) by N. Feans
78

89
## Demos
9-
* [Test Suite](http://29a.ch/sandbox/2014/smartcrop/examples/testsuite.html), contains over 100 images, **heavy**
10-
* [Test Bed](http://29a.ch/sandbox/2014/smartcrop/examples/testbed.html), allows you to upload your own images
10+
* [Test Suite](http://29a.ch/sandbox/2014/smartcrop/examples/testsuite.html), contains over 100 images, **heavy**.
11+
* [Test Bed](http://29a.ch/sandbox/2014/smartcrop/examples/testbed.html), allows you to test smartcrop with your own images.
1112
* [Photo transitions](http://29a.ch/sandbox/2014/smartcrop/examples/slideshow.html), automatically creates Ken Burns transitions for a slide show.
1213

1314
## Algorithm Overview
@@ -16,6 +17,7 @@ Smartcrop.js works using fairly dumb image processing. In short:
1617
1. Find edges using laplace
1718
1. Find regions with a color like skin
1819
1. Find regions high in saturation
20+
1. Boost regions as specified by options (for example detected faces)
1921
1. Generate a set of candidate crops using a sliding window
2022
1. Rank them using an importance function to focus the detail in the center
2123
and avoid it in the edges.
@@ -37,26 +39,28 @@ Output:
3739
```npm install smartcrop```
3840
or
3941
```bower install smartcrop```
40-
or just download [smartcrop.js](https://raw.githubusercontent.com/jwagner/smartcrop.js/master/smartcrop.js) from the git repo.
42+
or just download [smartcrop.js](https://raw.githubusercontent.com/jwagner/smartcrop.js/master/smartcrop.js) from the git repository.
4143

4244
Smarcrop requires support for [Promises](http://caniuse.com/#feat=promises),
43-
use a [polyfill](https://github.com/taylorhakes/promise-polyfill) for unsupported browsers or set `smartcrop.Promise` to your promise implementation
45+
use a [polyfill](https://github.com/taylorhakes/promise-polyfill) for unsupported browsers or set `smartcrop.Promise` to your favorite promise implementation
4446
(I recommend [bluebird](http://bluebirdjs.com/)).
4547

48+
## Command Line Interface
49+
The [smartcrop-cli](https://github.com/jwagner/smartcrop-cli) offers command line interface to smartcrop.js.
4650

47-
## CLI / Node.js
48-
The [smartcrop-cli](https://github.com/jwagner/smartcrop-cli) offers command line interface to smartcrop.js. It also serves as an example on how to use smartcrop
49-
from node.
51+
## Node
52+
You can use smartcrop from nodejs via either [smartcrop-gm](https://github.com/jwagner/smartcrop-gm) (which is using image magick via gm) or [smartcrop-sharp](https://github.com/jwagner/smartcrop-sharp) (which is using libvips via sharp).
53+
The [smartcrop-cli](https://github.com/jwagner/smartcrop-cli) can be used as an example of using smartcrop from node.
5054

51-
## Module Formats
55+
## Supported Module Formats
5256

53-
* common js
54-
* amd
57+
* CommonJS
58+
* AMD
5559
* global export / window
5660

5761
## Supported Browsers
5862

59-
See [caniuse.com/canvas](http://caniuse.com/canvas)
63+
See [caniuse.com/canvas](http://caniuse.com/canvas).
6064
A [polyfill](https://github.com/taylorhakes/promise-polyfill) for
6165
[Promises](http://caniuse.com/#feat=promises) is recommended.
6266

@@ -84,18 +88,26 @@ You may not use cross-domain images without [CORS](https://en.wikipedia.org/wiki
8488

8589
**height:** height of the crop you want to use.
8690

87-
**debug *(internal)*:** if true, cropResults will contain a debugCanvas.
91+
**boost:** optional array of regions whose 'interestingness' you want to boost (for example faces). See [boost](#boost);
8892

89-
There are many more (for now undocumented) options available. Check the [source](smartcrop.js#L32) and know that they might change in the future.
93+
**ruleOfThirds:** optional boolean if set to false it will turn off the rule of thirds composition weight.
94+
95+
**debug *(internal)*:** if true, cropResults will contain a debugCanvas and the complete results array.
96+
97+
There are many more (for now undocumented) options available.
98+
Check the [source](smartcrop.js#L32) and be advised that they might change in the future.
9099

91100
### cropResult
101+
Result of the promise returned by smartcrop.crop.
92102
```javascript
93103
{
94-
topCrop: crop,
95-
crops: [crop]
104+
topCrop: crop
96105
}
97106
```
107+
98108
### crop
109+
An invididual crop.
110+
99111
```javascript
100112
{
101113
x: 11, // pixels from the left side
@@ -105,8 +117,22 @@ There are many more (for now undocumented) options available. Check the [source]
105117
}
106118
```
107119

108-
## Tests
120+
### boost
121+
Describes a region to boost. A usage example of this is to take
122+
into account faces in the image. See [smartcrop-cli](https://github.com/jwagner/smartcrop-cli) for an example on how to integrate face detection.
109123

124+
```javascript
125+
{
126+
x: 11, // pixels from the left side
127+
y: 20, // pixels from the top
128+
width: 1, // pixels
129+
height: 1, // pixels
130+
weight: 1 // [0, 1]
131+
}
132+
```
133+
134+
135+
## Tests
110136
You can run the tests using grunt test. Alternatively you can also just run grunt (the default task) and open http://localhost:8000/test/.
111137
The test coverage for smartcrop.js is very limited at the moment. I expect to improve this as the code matures and the concepts solidify.
112138

@@ -128,5 +154,12 @@ In other words, it's fine to run it on one image, it's not cool to run it on an
128154
* [smartcrop.py](https://github.com/hhatto/smartcrop.py) by [Hideo Hattori](http://www.hexacosa.net/about/)
129155
* [smartcrop-rails](https://github.com/sadiqmmm/smartcrop-rails) smartcrop wrapped in a ruby gem by [Mohammed Sadiq](https://github.com/sadiqmmm/)
130156

157+
## Version history
158+
159+
### 1.0
160+
Refactoring/cleanup to make it easier to use with node.js (dropping the node-canvas dependency) and enable support for boosts which can be used to do face detection.
161+
This is a 1.0 in the semantic meaning (denoting backwards incompatible API changes).
162+
It does not denote a finished product.
163+
131164
## License
132165
Copyright (c) 2016 Jonas Wagner, licensed under the MIT License (enclosed)

examples/images/slideshow/0.jpg

453 KB
Loading

examples/images/slideshow/1.jpg

512 KB
Loading

examples/images/slideshow/2.jpg

430 KB
Loading

examples/images/slideshow/3.jpg

632 KB
Loading

examples/images/slideshow/4.jpg

450 KB
Loading

examples/images/slideshow/5.jpg

435 KB
Loading

examples/images/slideshow/6.jpg

192 KB
Loading

examples/images/slideshow/7.jpg

356 KB
Loading

examples/jquery.facedetection.min.js

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/smartcrop-debug.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
function debugDraw(result, showCrop) {
2+
var topCrop = result.debugTopCrop;
3+
var options = result.debugOptions;
4+
var output = result.debugOutput;
5+
var canvas = document.createElement('canvas');
6+
canvas.width = output.width;
7+
canvas.height = output.height;
8+
var ctx = canvas.getContext('2d');
9+
ctx.fillStyle = 'rgba(255, 0, 0, 0.1)';
10+
ctx.fillRect(topCrop.x, topCrop.y, topCrop.width, topCrop.height);
11+
var debugOutput = ctx.createImageData(output.width, output.height);
12+
debugOutput.data.set(output.data);
13+
for (var y = 0; y < output.height; y++) {
14+
for (var x = 0; x < output.width; x++) {
15+
var p = (y * output.width + x) * 4;
16+
if (showCrop) {
17+
var I = smartcrop.importance(options, topCrop, x, y);
18+
if (I > 0) {
19+
debugOutput.data[p + 1] += I * 32;
20+
}
21+
22+
if (I < 0) {
23+
debugOutput.data[p] += I * -64;
24+
}
25+
}
26+
// visualize alpha (boost) as magenta
27+
var boost = debugOutput.data[p + 3] / 2;
28+
debugOutput.data[p] += boost;
29+
debugOutput.data[p + 2] += boost;
30+
debugOutput.data[p + 3] = 255;
31+
}
32+
}
33+
34+
ctx.putImageData(debugOutput, 0, 0);
35+
ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';
36+
if (showCrop) {
37+
ctx.strokeRect(topCrop.x, topCrop.y, topCrop.width, topCrop.height);
38+
}
39+
if (options.boost) {
40+
ctx.strokeStyle = 'rgba(255, 255, 255, 1.0)';
41+
for (var i = 0; i < options.boost.length; i++) {
42+
var b = options.boost[i];
43+
ctx.strokeRect(b.x, b.y, b.width, b.height);
44+
}
45+
}
46+
return canvas;
47+
}

examples/style.css

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ input[type=file] {
2929
input[type=range] {
3030
width: 300px;
3131
}
32+
3233
img, canvas { padding: 8px; box-sizing: border-box; max-width: 100%; margin: auto; }
3334

3435
.output {

examples/testbed.html

+11-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ <h1>smartcrop.js testbed</h1>
1313
<input type=file name=file accept="image/*" >
1414
<label>Width<div><input name=width type="range" min=50 max=500 step=1 value=250 /><span class=value>250</span>px</div></label>
1515
<label>Height<div><input name=height type="range" min=50 max=500 step=1 value=250 /><span class=value>250</span>px</div></label>
16-
<label>minScale<div><input name=minScale type="range" min=0.5 max=1.0 step=0.1 value=0.9 /><span class=value>0.9</span></div></label>
16+
<label>minScale<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>
17+
<p><label>Rule of thirds<input name=ruleOfThirds type="checkbox" value=1 checked /></label></p>
18+
<p><strong>Face Detection</strong></p>
19+
<div><label><input name=faceDetection type=radio value=off checked />Off</label></div>
20+
<div><label><input name=faceDetection type=radio value=jquery />Use <a href="http://facedetection.jaysalvat.com/">jquery.facedetection</a></label></div>
21+
<div><label><input name=faceDetection type=radio value=tracking />Use <a href="https://trackingjs.com/">tracking.js</a></label></div>
1722
</form>
1823
</div>
1924
<h2>Drop images on this page to analyze them.</h2>
@@ -28,6 +33,10 @@ <h2>Drop images on this page to analyze them.</h2>
2833
<script src=jquery.js></script>
2934
<script src=underscore.js></script>
3035
<script src="../smartcrop.js"></script>
36+
<script src=smartcrop-debug.js></script>
37+
<script src=tracking-min.js></script>
38+
<script src=tracking-face-min.js></script>
39+
<script src=jquery.facedetection.min.js></script>
3140
<script src=testbed.js>
3241
</script>
3342
<script>
@@ -36,7 +45,7 @@ <h2>Drop images on this page to analyze them.</h2>
3645
_gaq.push(['_setDomainName', '29a.ch']);
3746
_gaq.push(['_setSiteSpeedSampleRate', 50]);
3847
_gaq.push(['_trackPageview']);
39-
48+
4049
</script>
4150
<script src="//www.google-analytics.com/ga.js" async></script>
4251
</body>

0 commit comments

Comments
 (0)