warper.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. const ImgWarper = ImgWarper || {};
  2. ImgWarper.Warper = function(img, multiplier, optGridSize, optAlpha) {
  3. this.alpha = optAlpha || 3;
  4. this.gridSize = multiplier || 1;
  5. this.multiplier = multiplier || 1;
  6. var that = this;
  7. // this is source canvas
  8. // that.cvs = ctx.canvas;
  9. // that._ctx = ctx;
  10. // and this is source imageData
  11. that.canvas = document.createElement('canvas');
  12. that.ctx = that.canvas.getContext("2d");
  13. that.canvas.width = img.width * this.multiplier;
  14. that.canvas.height =img.height * this.multiplier;
  15. that.ctx.drawImage(img, 0, 0, that.canvas.width, that.canvas.height);
  16. var imgData = that.ctx.getImageData(0, 0, that.canvas.width, that.canvas.height);
  17. that.width = that.canvas.width;
  18. that.height = that.canvas.height;
  19. that.imgData = imgData.data;
  20. that.bilinearInterpolation =
  21. new ImgWarper.BilinearInterpolation(that.width, that.height, that.canvas);
  22. that.ctx.setTransform(1, 0, 0, 1, 0, 0);
  23. that.ctx.clearRect(0, 0, img.width, img.height);
  24. that.ctx.putImageData(imgData, 0, 0, 0, 0, that.canvas.width, that.canvas.height);
  25. console.log('warper created');
  26. that.grid = [];
  27. for (var i = 0; i < that.width ; i += that.gridSize) {
  28. for (var j = 0; j < that.height ; j += that.gridSize) {
  29. var a = new ImgWarper.Point(i,j);
  30. var b = new ImgWarper.Point(i + that.gridSize, j);
  31. var c = new ImgWarper.Point(i + that.gridSize, j + that.gridSize);
  32. var d = new ImgWarper.Point(i, j + that.gridSize);
  33. that.grid.push([a, b, c, d]);
  34. }
  35. }
  36. return that;
  37. }
  38. ImgWarper.Warper.prototype.warp = function(fromPoints, toPoints) {
  39. var that = this;
  40. fromPoints = fromPoints.map(function(p){
  41. return new ImgWarper.Point(p.x * that.multiplier, p.y * that.multiplier);
  42. });
  43. toPoints = toPoints.map(function(p){
  44. return new ImgWarper.Point(p.x * that.multiplier, p.y * that.multiplier);
  45. });
  46. var deformation =
  47. new ImgWarper.AffineDeformation(toPoints, fromPoints, this.alpha);
  48. var transformedGrid = [];
  49. for (var i = 0; i < this.grid.length; ++i) {
  50. transformedGrid[i] = [
  51. deformation.pointMover(this.grid[i][0]),
  52. deformation.pointMover(this.grid[i][1]),
  53. deformation.pointMover(this.grid[i][2]),
  54. deformation.pointMover(this.grid[i][3])];
  55. }
  56. var newImg = this.bilinearInterpolation
  57. .generate(this.imgData, this.grid, transformedGrid);
  58. this.ctx.setTransform(1, 0, 0, 1, 0, 0);
  59. // this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  60. this.ctx.putImageData(newImg, 0, 0, 0, 0, this.canvas.width, this.canvas.height);
  61. // this._ctx.clearRect(0, 0, this.cvs.width, this.cvs.height);
  62. // this._ctx.drawImage(this.canvas, 0, 0, this.cvs.width, this.cvs.height);
  63. return this.canvas;
  64. }
  65. ImgWarper.Warper.prototype.drawGrid = function(fromPoints, toPoints) {
  66. // Forward warping.
  67. var deformation =
  68. new ImgWarper.AffineDeformation(fromPoints, toPoints, this.alpha);
  69. var context = this.canvas.getContext("2d");
  70. for (var i = 0; i < this.grid.length; ++i) {
  71. context.beginPath();
  72. var point = deformation.pointMover(this.grid[i][0]);
  73. context.moveTo(point.x, point.y);
  74. for (var j = 1; j < 4; ++j) {
  75. point = deformation.pointMover(this.grid[i][j]);
  76. context.lineTo(point.x, point.y);
  77. }
  78. context.strokeStyle = 'rgba(170, 170, 170, 0.5)';
  79. context.stroke();
  80. }
  81. }
  82. ImgWarper.AffineDeformation = function(fromPoints, toPoints, alpha) {
  83. this.w = null;
  84. this.pRelative = null;
  85. this.qRelative = null;
  86. this.A = null;
  87. if (fromPoints.length != toPoints.length) {
  88. console.error('Points are not of same length.');
  89. return;
  90. }
  91. this.n = fromPoints.length;
  92. this.fromPoints = fromPoints;
  93. this.toPoints = toPoints;
  94. this.alpha = alpha;
  95. };
  96. ImgWarper.AffineDeformation.prototype.pointMover = function (point){
  97. if (null == this.pRelative || this.pRelative.length < this.n) {
  98. this.pRelative = new Array(this.n);
  99. }
  100. if (null == this.qRelative || this.qRelative.length < this.n) {
  101. this.qRelative = new Array(this.n);
  102. }
  103. if (null == this.w || this.w.length < this.n) {
  104. this.w = new Array(this.n);
  105. }
  106. if (null == this.A || this.A.length < this.n) {
  107. this.A = new Array(this.n);
  108. }
  109. for (var i = 0; i < this.n; ++i) {
  110. var t = this.fromPoints[i].subtract(point);
  111. this.w[i] = Math.pow(t.x * t.x + t.y * t.y, -this.alpha);
  112. }
  113. var pAverage = ImgWarper.Point.weightedAverage(this.fromPoints, this.w);
  114. var qAverage = ImgWarper.Point.weightedAverage(this.toPoints, this.w);
  115. for (var i = 0; i < this.n; ++i) {
  116. this.pRelative[i] = this.fromPoints[i].subtract(pAverage);
  117. this.qRelative[i] = this.toPoints[i].subtract(qAverage);
  118. }
  119. var B = new ImgWarper.Matrix22(0, 0, 0, 0);
  120. for (var i = 0; i < this.n; ++i) {
  121. B.addM(this.pRelative[i].wXtX(this.w[i]));
  122. }
  123. B = B.inverse();
  124. for (var j = 0; j < this.n; ++j) {
  125. this.A[j] = point.subtract(pAverage).multiply(B)
  126. .dotP(this.pRelative[j]) * this.w[j];
  127. }
  128. var r = qAverage; //r is an point
  129. for (var j = 0; j < this.n; ++j) {
  130. r = r.add(this.qRelative[j].multiply_d(this.A[j]));
  131. }
  132. return r;
  133. };
  134. /*/ ----------------------------------------------------------- /*/
  135. /*/ ----------------------------------------------------------- /*/
  136. /*/ ----------------------------------------------------------- /*/
  137. /*/ ----------------------------------------------------------- /*/
  138. /*/ ----------------------------------------------------------- /*/
  139. ImgWarper.BilinearInterpolation = function(width, height, canvas){
  140. this.width = width;
  141. this.height = height;
  142. this.ctx = canvas.getContext("2d");
  143. this.imgTargetData = this.ctx.createImageData(this.width, this.height);
  144. };
  145. ImgWarper.BilinearInterpolation.prototype.generate =
  146. function(source, fromGrid, toGrid) {
  147. this.imgData = source;
  148. for (var i = 0; i < toGrid.length; ++i) {
  149. this.fill(toGrid[i], fromGrid[i]);
  150. }
  151. return this.imgTargetData;
  152. };
  153. ImgWarper.BilinearInterpolation.prototype.fill =
  154. function(sourcePoints, fillingPoints) {
  155. var i, j;
  156. var srcX, srcY;
  157. var x0 = fillingPoints[0].x;
  158. var x1 = fillingPoints[2].x;
  159. var y0 = fillingPoints[0].y;
  160. var y1 = fillingPoints[2].y;
  161. x0 = Math.max(x0, 0);
  162. y0 = Math.max(y0, 0);
  163. x1 = Math.min(x1, this.width - 1);
  164. y1 = Math.min(y1, this.height - 1);
  165. var xl, xr, topX, topY, bottomX, bottomY;
  166. var yl, yr, rgb, index;
  167. for (i = x0; i <= x1; ++i) {
  168. xl = (i - x0) / (x1 - x0);
  169. xr = 1 - xl;
  170. topX = xr * sourcePoints[0].x + xl * sourcePoints[1].x;
  171. topY = xr * sourcePoints[0].y + xl * sourcePoints[1].y;
  172. bottomX = xr * sourcePoints[3].x + xl * sourcePoints[2].x;
  173. bottomY = xr * sourcePoints[3].y + xl * sourcePoints[2].y;
  174. for (j = y0; j <= y1; ++j) {
  175. yl = (j - y0) / (y1 - y0);
  176. yr = 1 - yl;
  177. srcX = topX * yr + bottomX * yl;
  178. srcY = topY * yr + bottomY * yl;
  179. index = ((j * this.width) + i) * 4;
  180. if (srcX < 0 || srcX > this.width - 1 ||
  181. srcY < 0 || srcY > this.height - 1) {
  182. this.imgTargetData.data[index] = 255;
  183. this.imgTargetData.data[index + 1] = 255;
  184. this.imgTargetData.data[index + 2] = 255;
  185. this.imgTargetData.data[index + 3] = 0;
  186. continue;
  187. }
  188. var srcX1 = Math.floor(srcX);
  189. var srcY1 = Math.floor(srcY);
  190. var base = ((srcY1 * this.width) + srcX1) * 4;
  191. //rgb = this.nnquery(srcX, srcY);
  192. this.imgTargetData.data[index] = this.imgData[base];
  193. this.imgTargetData.data[index + 1] = this.imgData[base + 1];
  194. this.imgTargetData.data[index + 2] = this.imgData[base + 2];
  195. this.imgTargetData.data[index + 3] = this.imgData[base + 3];
  196. }
  197. }
  198. };
  199. ImgWarper.BilinearInterpolation.prototype.nnquery = function(x, y) {
  200. var x1 = Math.floor(x);
  201. var y1 = Math.floor(y);
  202. var base = ((y1 * this.width) + x1) * 4;
  203. return [
  204. this.imgData[base],
  205. this.imgData[base + 1],
  206. this.imgData[base + 2],
  207. this.imgData[base + 3]];
  208. };
  209. ImgWarper.BilinearInterpolation.prototype.query = function(x, y) {
  210. var x1,x2,y1,y2;
  211. x1 = Math.floor(x); x2 = Math.ceil(x);
  212. y1 = Math.floor(y); y2 = Math.ceil(y);
  213. var c = [0, 0, 0, 0]; // get new RGB
  214. var base11 = ((y1 * this.width) + x1) * 4;
  215. var base12 = ((y2 * this.width) + x1) * 4;
  216. var base21 = ((y1 * this.width) + x2) * 4;
  217. var base22 = ((y2 * this.width) + x2) * 4;
  218. // 4 channels: RGBA
  219. for (var i = 0; i < 4; ++i) {
  220. t11 = this.imgData[base11 + i];
  221. t12 = this.imgData[base12 + i];
  222. t21 = this.imgData[base21 + i];
  223. t22 = this.imgData[base22 + i];
  224. t = (t11 * (x2 - x) * (y2 - y) +
  225. t21 * (x - x1) * (y2 - y) +
  226. t12 * (x2 - x) * (y - y1) +
  227. t22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1));
  228. c[i] = parseInt(t);
  229. }
  230. return c;
  231. };
  232. /*/ ----------------------------------------------------------- /*/
  233. /*/ ----------------------------------------------------------- /*/
  234. /*/ ----------------------------------------------------------- /*/
  235. /*/ ----------------------------------------------------------- /*/
  236. /*/ ----------------------------------------------------------- /*/
  237. ImgWarper.Matrix22 = function(N11, N12, N21, N22) {
  238. this.M11 = N11;
  239. this.M12 = N12;
  240. this.M21 = N21;
  241. this.M22 = N22;
  242. };
  243. ImgWarper.Matrix22.prototype.adjugate = function () {
  244. return new ImgWarper.Matrix22(
  245. this.M22, -this.M12,
  246. -this.M21, this.M11);
  247. };
  248. ImgWarper.Matrix22.prototype.determinant = function () {
  249. return this.M11 * this.M22 - this.M12 * this.M21;
  250. };
  251. ImgWarper.Matrix22.prototype.multiply = function (m) {
  252. this.M11 *= m;
  253. this.M12 *= m;
  254. this.M21 *= m;
  255. this.M22 *= m;
  256. return this;
  257. };
  258. ImgWarper.Matrix22.prototype.addM = function(o) {
  259. this.M11 += o.M11;
  260. this.M12 += o.M12;
  261. this.M21 += o.M21;
  262. this.M22 += o.M22;
  263. };
  264. ImgWarper.Matrix22.prototype.inverse = function () {
  265. return this.adjugate().multiply(1.0 / this.determinant());
  266. };
  267. /*/ ----------------------------------------------------------- /*/
  268. /*/ ----------------------------------------------------------- /*/
  269. /*/ ----------------------------------------------------------- /*/
  270. /*/ ----------------------------------------------------------- /*/
  271. /*/ ----------------------------------------------------------- /*/
  272. ImgWarper.Point = function (x, y) {
  273. this.x = x;
  274. this.y = y;
  275. };
  276. ImgWarper.Point.prototype.add = function (o) {
  277. return new ImgWarper.Point(this.x + o.x, this.y + o.y);
  278. };
  279. ImgWarper.Point.prototype.subtract = function (o) {
  280. return new ImgWarper.Point(this.x - o.x, this.y - o.y);
  281. };
  282. // w * [x; y] * [x, y]
  283. ImgWarper.Point.prototype.wXtX = function (w) {
  284. return (new ImgWarper.Matrix22(
  285. this.x * this.x * w, this.x * this.y * w,
  286. this.y * this.x * w, this.y * this.y * w
  287. ));
  288. };
  289. // Dot product
  290. ImgWarper.Point.prototype.dotP = function (o) {
  291. return this.x * o.x + this.y * o.y;
  292. };
  293. ImgWarper.Point.prototype.multiply = function (o) {
  294. return new ImgWarper.Point(
  295. this.x * o.M11 + this.y * o.M21, this.x * o.M12 + this.y * o.M22);
  296. };
  297. ImgWarper.Point.prototype.multiply_d = function (o) {
  298. return new ImgWarper.Point(this.x * o, this.y * o);
  299. };
  300. ImgWarper.Point.weightedAverage = function (p, w) {
  301. var i;
  302. var sx = 0,
  303. sy = 0,
  304. sw = 0;
  305. for (i = 0; i < p.length; i++) {
  306. sx += p[i].x * w[i];
  307. sy += p[i].y * w[i];
  308. sw += w[i];
  309. }
  310. return new ImgWarper.Point(sx / sw, sy / sw);
  311. };
  312. ImgWarper.Point.prototype.InfintyNormDistanceTo = function (o) {
  313. return Math.max(Math.abs(this.x - o.x), Math.abs(this.y - o.y));
  314. }
  315. export default ImgWarper;