-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbicubic.js
288 lines (245 loc) · 9.39 KB
/
bicubic.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/**
* 缩放算法
* 双立方(三次)卷积插值,图像更真实
* 计算周围16个点
* 取一阶导数值为二阶差分值的情况,满足插值函数一阶导函数连续
* 函数逼近程度和三次样条插值效果一样,非常的高
*
* 公式:(矩阵乘法)
* 推导公式
* http://blog.csdn.net/qq_24451605/article/details/49474113
* https://en.wikipedia.org/wiki/Bicubic_interpolation
* */
/**
* 采样公式的常数A取值,调整锐化与模糊
* -0.5 三次Hermite样条
* -0.75 常用值之一
* -1 逼近y = sin(x*PI)/(x*PI)
* -2 常用值之一
*/
const A = -0.1;
function interpolationCalculate(x) {
const absX = x >= 0 ? x : -x;
const x2 = x * x;
const x3 = absX * x2;
if (absX <= 1) {
return 1 - (A + 3) * x2 + (A + 2) * x3;
} else if (absX <= 2) {
return -4 * A + 8 * A * absX - 5 * A * x2 + A * x3;
}
return 0;
}
function bicubic_unrolled(pixels, x, y, width) {
var a, b, c, d, v0, v1, v2, v3, r, g, b;
var fx = x ^ 0;
var fy = y ^ 0;
var percentX = x - fx;
var percentY = y - fy;
var fx14 = fx * 4;
var fx04 = fx14 - 4;
var fx24 = fx14 + 4;
var fx34 = fx14 + 8;
var w4 = width * 4;
var yw14r = fy * w4;
var yw04r = yw14r - w4;
var yw24r = yw14r + w4;
var yw34r = yw14r + w4 + w4;
var yw14g = yw14r + 1;
var yw04g = yw04r + 1;
var yw24g = yw24r + 1;
var yw34g = yw34r + 1;
var yw14b = yw14r + 2;
var yw04b = yw04r + 2;
var yw24b = yw24r + 2;
var yw34b = yw34r + 2;
// Red
a = pixels[yw04r + fx04];
b = pixels[yw04r + fx14];
c = pixels[yw04r + fx24];
d = pixels[yw04r + fx34];
v0 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v0 = v0 > 255 ? 255 : v0 < 0 ? 0 : v0;
a = pixels[yw14r + fx04];
b = pixels[yw14r + fx14];
c = pixels[yw14r + fx24];
d = pixels[yw14r + fx34];
v1 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v1 = v1 > 255 ? 255 : v1 < 0 ? 0 : v1;
a = pixels[yw24r + fx04];
b = pixels[yw24r + fx14];
c = pixels[yw24r + fx24];
d = pixels[yw24r + fx34];
v2 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v2 = v2 > 255 ? 255 : v2 < 0 ? 0 : v2;
a = pixels[yw34r + fx04];
b = pixels[yw34r + fx14];
c = pixels[yw34r + fx24];
d = pixels[yw34r + fx34];
v3 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v3 = v3 > 255 ? 255 : v3 < 0 ? 0 : v3;
a = v0;
b = v1;
c = v2;
d = v3;
r = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentY) * percentY) * percentY + b;
r = r > 255 ? 255 : r < 0 ? 0 : r ^ 0;
// Green
a = pixels[yw04g + fx04];
b = pixels[yw04g + fx14];
c = pixels[yw04g + fx24];
d = pixels[yw04g + fx34];
v0 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v0 = v0 > 255 ? 255 : v0 < 0 ? 0 : v0;
a = pixels[yw14g + fx04];
b = pixels[yw14g + fx14];
c = pixels[yw14g + fx24];
d = pixels[yw14g + fx34];
v1 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v1 = v1 > 255 ? 255 : v1 < 0 ? 0 : v1;
a = pixels[yw24g + fx04];
b = pixels[yw24g + fx14];
c = pixels[yw24g + fx24];
d = pixels[yw24g + fx34];
v2 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v2 = v2 > 255 ? 255 : v2 < 0 ? 0 : v2;
a = pixels[yw34g + fx04];
b = pixels[yw34g + fx14];
c = pixels[yw34g + fx24];
d = pixels[yw34g + fx34];
v3 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v3 = v3 > 255 ? 255 : v3 < 0 ? 0 : v3;
a = v0;
b = v1;
c = v2;
d = v3;
g = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentY) * percentY) * percentY + b;
g = g > 255 ? 255 : g < 0 ? 0 : g ^ 0;
// Blue
a = pixels[yw04b + fx04];
b = pixels[yw04b + fx14];
c = pixels[yw04b + fx24];
d = pixels[yw04b + fx34];
v0 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v0 = v0 > 255 ? 255 : v0 < 0 ? 0 : v0;
a = pixels[yw14b + fx04];
b = pixels[yw14b + fx14];
c = pixels[yw14b + fx24];
d = pixels[yw14b + fx34];
v1 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v1 = v1 > 255 ? 255 : v1 < 0 ? 0 : v1;
a = pixels[yw24b + fx04];
b = pixels[yw24b + fx14];
c = pixels[yw24b + fx24];
d = pixels[yw24b + fx34];
v2 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v2 = v2 > 255 ? 255 : v2 < 0 ? 0 : v2;
a = pixels[yw34b + fx04];
b = pixels[yw34b + fx14];
c = pixels[yw34b + fx24];
d = pixels[yw34b + fx34];
v3 = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentX) * percentX) * percentX + b;
v3 = v3 > 255 ? 255 : v3 < 0 ? 0 : v3;
a = v0;
b = v1;
c = v2;
d = v3;
b = 0.5 * (c - a + (2.0 * a - 5.0 * b + 4.0 * c - d + (3.0 * (b - c) + d - a) * percentY) * percentY) * percentY + b;
b = b > 255 ? 255 : b < 0 ? 0 : b ^ 0;
return [r, g, b];
}
function getPixelValue(pixelValue) {
let newPixelValue = pixelValue;
newPixelValue = Math.min(255, newPixelValue);
newPixelValue = Math.max(0, newPixelValue);
return newPixelValue;
}
/**
* 获取某行某列的像素对于的rgba值
* @param {Object} data 图像数据
* @param {Number} srcWidth 宽度
* @param {Number} srcHeight 高度
* @param {Number} row 目标像素的行
* @param {Number} col 目标像素的列
*/
function getRGBAValue(data, srcWidth, srcHeight, row, col) {
let newRow = row;
let newCol = col;
if (newRow >= srcHeight) {
newRow = srcHeight - 1;
} else if (newRow < 0) {
newRow = 0;
}
if (newCol >= srcWidth) {
newCol = srcWidth - 1;
} else if (newCol < 0) {
newCol = 0;
}
let newIndex = (newRow * srcWidth) + newCol;
newIndex *= 4;
return [
data[newIndex + 0],
data[newIndex + 1],
data[newIndex + 2],
data[newIndex + 3],
];
}
function scale(data, width, height, newWidth, newHeight) {
var dstData = new Uint8ClampedArray(newWidth * newHeight * 4);
console.log(dstData);
// 计算压缩后的缩放比
const scaleW = newWidth / width;
const scaleH = newHeight / height;
const filter = (dstCol, dstRow) => {
// 源图像中的坐标(可能是一个浮点)
const srcCol = Math.min(width - 1, dstCol / scaleW);
const srcRow = Math.min(height - 1, dstRow / scaleH);
const intCol = Math.floor(srcCol);
const intRow = Math.floor(srcRow);
/*
let dstI = (dstRow * newWidth) + dstCol;
dstI *= 4;
var rgb= bicubic_unrolled(data,srcRow,srcCol,4);
dstData[dstI + 0] = rgb[0];
dstData[dstI + 1] = rgb[1];
dstData[dstI + 2] = rgb[2];
dstData[dstI + 3] = 255;
*/
// 计算u和v
const u = srcCol - intCol;
const v = srcRow - intRow;
// 真实的index,因为数组是一维的
let dstI = (dstRow * newWidth) + dstCol;
dstI *= 4;
// 存储灰度值的权重卷积和
const rgbaData = [0, 0, 0, 0];
// 根据数学推导,16个点的f1*f2加起来是趋近于1的(可能会有浮点误差)
// 因此就不再单独先加权值,再除了
// 16个邻近点
for (let m = -1; m <= 2; m += 1) {
for (let n = -1; n <= 2; n += 1) {
const rgba = getRGBAValue(data,width,height,intRow + m,intCol + n);
// 一定要正确区分 m,n和u,v对应的关系,否则会造成图像严重偏差(譬如出现噪点等)
// F(row + m, col + n)S(m - v)S(n - u)
const f1 = interpolationCalculate(m - v);
const f2 = interpolationCalculate(n - u);
const weight = f1 * f2;
rgbaData[0] += rgba[0] * weight;
rgbaData[1] += rgba[1] * weight;
rgbaData[2] += rgba[2] * weight;
rgbaData[3] += rgba[3] * weight;
}
}
dstData[dstI + 0] = getPixelValue(rgbaData[0]);
dstData[dstI + 1] = getPixelValue(rgbaData[1]);
dstData[dstI + 2] = getPixelValue(rgbaData[2]);
dstData[dstI + 3] = getPixelValue(rgbaData[3]);
};
// 区块
for (let col = 0; col < newWidth; col += 1) {
for (let row = 0; row < newHeight; row += 1) {
filter(col, row);
}
}
console.log(dstData);
return new ImageData(dstData, newWidth, newHeight);
}