-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgrafi-convolution.js
150 lines (134 loc) · 4.92 KB
/
grafi-convolution.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
;(function () {
/**
## ImageData object constructor
Every return from grafi method is formatted to an ImageData object.
This constructor is used when `window` is not available.
*/
function ImageData (pixelData, width, height) {
this.width = width
this.height = height
this.data = pixelData
}
/**
## formatter
Internal function used to format pixel data into ImageData object
### Parameters
- pixelData `Uint8ClampedArray`: pixel representation of the image
- width `Number`: width of the image
- hight `Number`: height of the image
### Example
formatter(new Uint8ClampedArray[400], 10, 10)
// ImageData { data: Uint8ClampedArray[400], width: 10, height: 10, }
*/
function formatter (pixelData, width, height) {
var colorDepth = pixelData.length / (width * height)
// Length of pixelData must be multipul of available pixels (width * height).
// Maximum color depth allowed is 4 (RGBA)
if (Math.round(colorDepth) !== colorDepth || colorDepth > 4) {
throw new Error('data and size of the image does now match')
}
if (!(pixelData instanceof Uint8ClampedArray)) {
throw new Error('pixel data passed is not an Uint8ClampedArray')
}
// If window is avilable create ImageData using browser API,
// otherwise call ImageData constructor
if (typeof window === 'object' && colorDepth === 4) {
return new window.ImageData(pixelData, width, height)
}
return new ImageData(pixelData, width, height)
}
/**
## convolution method
Internal method to apply convolution filter
!!! this method does not return ImageObject
### Parameters
- imageData `Object`: ImageData object
- option `Object` : Option object
### Example
//code sample goes here
*/
function convolution (imgData, option) {
// check options object & set default variables
option = option || {}
option.monochrome = option.monochrome || false
option.divisor = option.divisor || 1
if (!option.filter || !option.radius) {
throw new Error('Required options missing. filter : ' + option.filter + ', radius: ' + option.radius)
}
// Check length of data & avilable pixel size to make sure data is good data
var pixelSize = imgData.width * imgData.height
var dataLength = imgData.data.length
var colorDepth = dataLength / pixelSize
if (colorDepth !== 4 && colorDepth !== 1) {
throw new Error('ImageObject has incorrect color depth')
}
var newPixelData = new Uint8ClampedArray(pixelSize * (option.monochrome || 4))
var height = imgData.height
var width = imgData.width
var f = option.filter
var r = option.radius
var ch, y, x, fy, fx, arr, sum, i
// do convolution math for each channel
for (ch = 0; ch < colorDepth; ch++) {
for (y = r; y < height - r; y++) {
for (x = r; x < width - r; x++) {
i = (x + y * width) * colorDepth + ch
if (ch === 3) {
if (colorDepth === 4 && option.monochrome) {
newPixelData[x + y * width] = imgData.data[x + y * width]
continue
}
newPixelData[i] = imgData.data[i]
continue
}
arr = []
for (fy = -r; fy < r * 2; fy++) {
for (fx = -r; fx < r * 2; fx++) {
arr.push(imgData.data[(x + fx + (y + fy) * width) * colorDepth + ch])
}
}
sum = arr.map(function (data, index) { return data * f[index]}).reduce(function (p, n) { return p + n })
if (colorDepth === 4 && option.monochrome) {
newPixelData[(x + y * width)] = sum / option.divisor
continue
}
newPixelData[i] = sum / option.divisor
}
}
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
if (colorDepth === 4 && option.monochrome) {
// copy colors from top & bottom rows
if (y < r || y > height - (r * 2)) {
newPixelData[x + y * width] = imgData.data[x + y * width]
continue
}
// copy colors from left and write columns
if (x < r || x > width - (r * 2)) {
newPixelData[x + y * width] = imgData.data[x + y * width]
}
continue
}
i = (x + y * width) * colorDepth + ch
// copy colors from top & bottom rows
if (y < r || y > height - (r * 2)) {
newPixelData[i] = imgData.data[i]
continue
}
// copy colors from left and write columns
if (x < r || x > width - (r * 2)) {
newPixelData[i] = imgData.data[i]
}
}
}
}
return formatter(newPixelData, imgData.width, imgData.height)
}
var grafi = {}
grafi.convolution = convolution
if (typeof module === 'object' && module.exports) {
module.exports = grafi
} else {
this.grafi = grafi
}
}())