Source: utils/Image.js

(function() {
 /**
 * Image utility.
 * @static
 * @constructor
 */
 tracking.Image = {};
 /**
 * Computes gaussian blur. Adpated from
 * https://github.com/kig/canvasfilters.
 * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
 * @param {number} width The image width.
 * @param {number} height The image height.
 * @param {number} diameter Gaussian blur diameter, must be greater than 1.
 * @return {array} The edge pixels in a linear [r,g,b,a,...] array.
 */
 tracking.Image.blur = function(pixels, width, height, diameter) {
 diameter = Math.abs(diameter);
 if (diameter <= 1) {
 throw new Error('Diameter should be greater than 1.');
 }
 var radius = diameter / 2;
 var len = Math.ceil(diameter) + (1 - (Math.ceil(diameter) % 2));
 var weights = new Float32Array(len);
 var rho = (radius + 0.5) / 3;
 var rhoSq = rho * rho;
 var gaussianFactor = 1 / Math.sqrt(2 * Math.PI * rhoSq);
 var rhoFactor = -1 / (2 * rho * rho);
 var wsum = 0;
 var middle = Math.floor(len / 2);
 for (var i = 0; i < len; i++) {
 var x = i - middle;
 var gx = gaussianFactor * Math.exp(x * x * rhoFactor);
 weights[i] = gx;
 wsum += gx;
 }
 for (var j = 0; j < weights.length; j++) {
 weights[j] /= wsum;
 }
 return this.separableConvolve(pixels, width, height, weights, weights, false);
 };
 /**
 * Computes the integral image for summed, squared, rotated and sobel pixels.
 * @param {array} pixels The pixels in a linear [r,g,b,a,...] array to loop
 * through.
 * @param {number} width The image width.
 * @param {number} height The image height.
 * @param {array} opt_integralImage Empty array of size `width * height` to
 * be filled with the integral image values. If not specified compute sum
 * values will be skipped.
 * @param {array} opt_integralImageSquare Empty array of size `width *
 * height` to be filled with the integral image squared values. If not
 * specified compute squared values will be skipped.
 * @param {array} opt_tiltedIntegralImage Empty array of size `width *
 * height` to be filled with the rotated integral image values. If not
 * specified compute sum values will be skipped.
 * @param {array} opt_integralImageSobel Empty array of size `width *
 * height` to be filled with the integral image of sobel values. If not
 * specified compute sobel filtering will be skipped.
 * @static
 */
 tracking.Image.computeIntegralImage = function(pixels, width, height, opt_integralImage, opt_integralImageSquare, opt_tiltedIntegralImage, opt_integralImageSobel) {
 if (arguments.length < 4) {
 throw new Error('You should specify at least one output array in the order: sum, square, tilted, sobel.');
 }
 var pixelsSobel;
 if (opt_integralImageSobel) {
 pixelsSobel = tracking.Image.sobel(pixels, width, height);
 }
 for (var i = 0; i < height; i++) {
 for (var j = 0; j < width; j++) {
 var w = i * width * 4 + j * 4;
 var pixel = ~~(pixels[w] * 0.299 + pixels[w + 1] * 0.587 + pixels[w + 2] * 0.114);
 if (opt_integralImage) {
 this.computePixelValueSAT_(opt_integralImage, width, i, j, pixel);
 }
 if (opt_integralImageSquare) {
 this.computePixelValueSAT_(opt_integralImageSquare, width, i, j, pixel * pixel);
 }
 if (opt_tiltedIntegralImage) {
 var w1 = w - width * 4;
 var pixelAbove = ~~(pixels[w1] * 0.299 + pixels[w1 + 1] * 0.587 + pixels[w1 + 2] * 0.114);
 this.computePixelValueRSAT_(opt_tiltedIntegralImage, width, i, j, pixel, pixelAbove || 0);
 }
 if (opt_integralImageSobel) {
 this.computePixelValueSAT_(opt_integralImageSobel, width, i, j, pixelsSobel[w]);
 }
 }
 }
 };
 /**
 * Helper method to compute the rotated summed area table (RSAT) by the
 * formula:
 *
 * RSAT(x, y) = RSAT(x-1, y-1) + RSAT(x+1, y-1) - RSAT(x, y-2) + I(x, y) + I(x, y-1)
 *
 * @param {number} width The image width.
 * @param {array} RSAT Empty array of size `width * height` to be filled with
 * the integral image values. If not specified compute sum values will be
 * skipped.
 * @param {number} i Vertical position of the pixel to be evaluated.
 * @param {number} j Horizontal position of the pixel to be evaluated.
 * @param {number} pixel Pixel value to be added to the integral image.
 * @static
 * @private
 */
 tracking.Image.computePixelValueRSAT_ = function(RSAT, width, i, j, pixel, pixelAbove) {
 var w = i * width + j;
 RSAT[w] = (RSAT[w - width - 1] || 0) + (RSAT[w - width + 1] || 0) - (RSAT[w - width - width] || 0) + pixel + pixelAbove;
 };
 /**
 * Helper method to compute the summed area table (SAT) by the formula:
 *
 * SAT(x, y) = SAT(x, y-1) + SAT(x-1, y) + I(x, y) - SAT(x-1, y-1)
 *
 * @param {number} width The image width.
 * @param {array} SAT Empty array of size `width * height` to be filled with
 * the integral image values. If not specified compute sum values will be
 * skipped.
 * @param {number} i Vertical position of the pixel to be evaluated.
 * @param {number} j Horizontal position of the pixel to be evaluated.
 * @param {number} pixel Pixel value to be added to the integral image.
 * @static
 * @private
 */
 tracking.Image.computePixelValueSAT_ = function(SAT, width, i, j, pixel) {
 var w = i * width + j;
 SAT[w] = (SAT[w - width] || 0) + (SAT[w - 1] || 0) + pixel - (SAT[w - width - 1] || 0);
 };
 /**
 * Converts a color from a colorspace based on an RGB color model to a
 * grayscale representation of its luminance. The coefficients represent the
 * measured intensity perception of typical trichromat humans, in
 * particular, human vision is most sensitive to green and least sensitive
 * to blue.
 * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
 * @param {number} width The image width.
 * @param {number} height The image height.
 * @param {boolean} fillRGBA If the result should fill all RGBA values with the gray scale
 * values, instead of returning a single value per pixel.
 * @param {Uint8ClampedArray} The grayscale pixels in a linear array ([p,p,p,a,...] if fillRGBA
 * is true and [p1, p2, p3, ...] if fillRGBA is false).
 * @static
 */
 tracking.Image.grayscale = function(pixels, width, height, fillRGBA) {
 var gray = new Uint8ClampedArray(fillRGBA ? pixels.length : pixels.length>> 2);
 var p = 0;
 var w = 0;
 for (var i = 0; i < height; i++) {
 for (var j = 0; j < width; j++) {
 var value = pixels[w] * 0.299 + pixels[w + 1] * 0.587 + pixels[w + 2] * 0.114;
 gray[p++] = value;
 if (fillRGBA) {
 gray[p++] = value;
 gray[p++] = value;
 gray[p++] = pixels[w + 3];
 }
 w += 4;
 }
 }
 return gray;
 };
 /**
 * Fast horizontal separable convolution. A point spread function (PSF) is
 * said to be separable if it can be broken into two one-dimensional
 * signals: a vertical and a horizontal projection. The convolution is
 * performed by sliding the kernel over the image, generally starting at the
 * top left corner, so as to move the kernel through all the positions where
 * the kernel fits entirely within the boundaries of the image. Adpated from
 * https://github.com/kig/canvasfilters.
 * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
 * @param {number} width The image width.
 * @param {number} height The image height.
 * @param {array} weightsVector The weighting vector, e.g [-1,0,1].
 * @param {number} opaque
 * @return {array} The convoluted pixels in a linear [r,g,b,a,...] array.
 */
 tracking.Image.horizontalConvolve = function(pixels, width, height, weightsVector, opaque) {
 var side = weightsVector.length;
 var halfSide = Math.floor(side / 2);
 var output = new Float32Array(width * height * 4);
 var alphaFac = opaque ? 1 : 0;
 for (var y = 0; y < height; y++) {
 for (var x = 0; x < width; x++) {
 var sy = y;
 var sx = x;
 var offset = (y * width + x) * 4;
 var r = 0;
 var g = 0;
 var b = 0;
 var a = 0;
 for (var cx = 0; cx < side; cx++) {
 var scy = sy;
 var scx = Math.min(width - 1, Math.max(0, sx + cx - halfSide));
 var poffset = (scy * width + scx) * 4;
 var wt = weightsVector[cx];
 r += pixels[poffset] * wt;
 g += pixels[poffset + 1] * wt;
 b += pixels[poffset + 2] * wt;
 a += pixels[poffset + 3] * wt;
 }
 output[offset] = r;
 output[offset + 1] = g;
 output[offset + 2] = b;
 output[offset + 3] = a + alphaFac * (255 - a);
 }
 }
 return output;
 };
 /**
 * Fast vertical separable convolution. A point spread function (PSF) is
 * said to be separable if it can be broken into two one-dimensional
 * signals: a vertical and a horizontal projection. The convolution is
 * performed by sliding the kernel over the image, generally starting at the
 * top left corner, so as to move the kernel through all the positions where
 * the kernel fits entirely within the boundaries of the image. Adpated from
 * https://github.com/kig/canvasfilters.
 * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
 * @param {number} width The image width.
 * @param {number} height The image height.
 * @param {array} weightsVector The weighting vector, e.g [-1,0,1].
 * @param {number} opaque
 * @return {array} The convoluted pixels in a linear [r,g,b,a,...] array.
 */
 tracking.Image.verticalConvolve = function(pixels, width, height, weightsVector, opaque) {
 var side = weightsVector.length;
 var halfSide = Math.floor(side / 2);
 var output = new Float32Array(width * height * 4);
 var alphaFac = opaque ? 1 : 0;
 for (var y = 0; y < height; y++) {
 for (var x = 0; x < width; x++) {
 var sy = y;
 var sx = x;
 var offset = (y * width + x) * 4;
 var r = 0;
 var g = 0;
 var b = 0;
 var a = 0;
 for (var cy = 0; cy < side; cy++) {
 var scy = Math.min(height - 1, Math.max(0, sy + cy - halfSide));
 var scx = sx;
 var poffset = (scy * width + scx) * 4;
 var wt = weightsVector[cy];
 r += pixels[poffset] * wt;
 g += pixels[poffset + 1] * wt;
 b += pixels[poffset + 2] * wt;
 a += pixels[poffset + 3] * wt;
 }
 output[offset] = r;
 output[offset + 1] = g;
 output[offset + 2] = b;
 output[offset + 3] = a + alphaFac * (255 - a);
 }
 }
 return output;
 };
 /**
 * Fast separable convolution. A point spread function (PSF) is said to be
 * separable if it can be broken into two one-dimensional signals: a
 * vertical and a horizontal projection. The convolution is performed by
 * sliding the kernel over the image, generally starting at the top left
 * corner, so as to move the kernel through all the positions where the
 * kernel fits entirely within the boundaries of the image. Adpated from
 * https://github.com/kig/canvasfilters.
 * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
 * @param {number} width The image width.
 * @param {number} height The image height.
 * @param {array} horizWeights The horizontal weighting vector, e.g [-1,0,1].
 * @param {array} vertWeights The vertical vector, e.g [-1,0,1].
 * @param {number} opaque
 * @return {array} The convoluted pixels in a linear [r,g,b,a,...] array.
 */
 tracking.Image.separableConvolve = function(pixels, width, height, horizWeights, vertWeights, opaque) {
 var vertical = this.verticalConvolve(pixels, width, height, vertWeights, opaque);
 return this.horizontalConvolve(vertical, width, height, horizWeights, opaque);
 };
 /**
 * Compute image edges using Sobel operator. Computes the vertical and
 * horizontal gradients of the image and combines the computed images to
 * find edges in the image. The way we implement the Sobel filter here is by
 * first grayscaling the image, then taking the horizontal and vertical
 * gradients and finally combining the gradient images to make up the final
 * image. Adpated from https://github.com/kig/canvasfilters.
 * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
 * @param {number} width The image width.
 * @param {number} height The image height.
 * @return {array} The edge pixels in a linear [r,g,b,a,...] array.
 */
 tracking.Image.sobel = function(pixels, width, height) {
 pixels = this.grayscale(pixels, width, height, true);
 var output = new Float32Array(width * height * 4);
 var sobelSignVector = new Float32Array([-1, 0, 1]);
 var sobelScaleVector = new Float32Array([1, 2, 1]);
 var vertical = this.separableConvolve(pixels, width, height, sobelSignVector, sobelScaleVector);
 var horizontal = this.separableConvolve(pixels, width, height, sobelScaleVector, sobelSignVector);
 for (var i = 0; i < output.length; i += 4) {
 var v = vertical[i];
 var h = horizontal[i];
 var p = Math.sqrt(h * h + v * v);
 output[i] = p;
 output[i + 1] = p;
 output[i + 2] = p;
 output[i + 3] = 255;
 }
 return output;
 };
}());

AltStyle によって変換されたページ (->オリジナル) /