Javascript图像处理之平滑处理

  原理来自Justany_WhiteSnowJavascript图像处理——平滑处理一文。

/*
 * BORDER_REPLICATE:     aaaaaaaa|abcdefgh|hhhhhhhh
 * BORDER_REFLECT:       hgfedcba|abcdefgh|hgfedcba
 * BORDER_REFLECT_101:   hgfedcb|abcdefgh|gfedcba
 * BORDER_WRAP:          efgh|abcdefgh|abcd
 * BORDER_CONSTANT:      iiiiiiii|abcdefgh|iiiiiiii  with some specified 'i'(default value [0, 0, 0, 255])
*/

(function () {
    function imageBlur(iCanvas, url, borderType, orientation, blurType, value) {
        this.canvas = iCanvas;
        this.iCtx = this.canvas.getContext("2d");
        this.url = url;
        this.borderType = borderType;
        this.orientation = orientation || "bottom";
        this.value = value || [0, 0, 0, 255];
        this.blurType = blurType || "blur";
    }

    imageBlur.prototype = {
        imread: function (_image) {
            var width = _image.width,
                height = _image.height;
            this.iResize(width, height);
            this.iCtx.drawImage(_image, 0, 0);
            var imageData = this.iCtx.getImageData(0, 0, width, height),
                tempMat = new Mat(height, width, imageData.data);
            imageData = null;
            this.iCtx.clearRect(0, 0, width, height);
            return tempMat;
        },
        iResize: function (_width, _height) {
            this.canvas.width = _width;
            this.canvas.height = _height;
        },
        RGBA2ImageData: function (_imgMat) {
            var width = _imgMat.col,
                height = _imgMat.row;
            var imageData = this.iCtx.createImageData(width, height);
            imageData.data.set(_imgMat.data);
            return imageData;
        },
        render: function () {
            var img = new Image();
            var _this = this;
            img.onload = function () {
                var myMat = _this.imread(img);
                var newImage = null;
                if (_this.blurType == "blur") {
                    newImage = blur(myMat, 11, 11, _this.borderType);
                } else if (_this.blurType == "median") {
                    newImage = medianBlur(myMat, 11, 11, _this.borderType);
                } else {
                    newImage = GaussianBlur(myMat, 11, 11, 0, 0, _this.borderType);
                }
                var newIamgeData = _this.RGBA2ImageData(newImage);
                _this.iCtx.putImageData(newIamgeData, 0, 0);
            };
            img.src = this.url;
        }
    };

    function Mat(_row, _col, _data, _buffer) {
        this.row = _row || 0;
        this.col = _col || 0;
        this.channel = 4;
        this.buffer = _buffer || new ArrayBuffer(_row * _col * 4);
        this.data = new Uint8ClampedArray(this.buffer);
        _data && this.data.set(_data);
        this.bytes = 1;
        this.type = "CV_RGBA";
    }

    function blur(__src, __size1, __size2, __borderType, __dst) {
        if (__src.type && __src.type == "CV_RGBA") {
            var height = __src.row,
                width = __src.col,
                dst = __dst || new Mat(height, width, __src.data),
                dstData = dst.data;
            var size1 = __size1 || 3,
                size2 = __size2 || size1,
                size = size1 * size2;
            if (size1 % 2 !== 1 || size2 % 2 !== 1) {
                console.error("size大小必须是奇数");
                return __src;
            }
            var startX = Math.floor(size1 / 2),
                startY = Math.floor(size2 / 2);
            var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType),
                mData = withBorderMat.data,
                mWidth = withBorderMat.col;

            var newValue, nowX, offsetY, offsetI;
            var i, j, c, y, x;

            for (i = height; i--;) {
                offsetI = i * width;
                for (j = width; j--;) {
                    for (c = 3; c--;) {
                        newValue = 0;
                        for (y = size2; y--;) {
                            offsetY = (y + i) * mWidth * 4;
                            for (x = size1; x--;) {
                                nowX = (x + j) * 4 + c;
                                newValue += mData[offsetY + nowX];
                            }
                        }
                        dstData[(j + offsetI) * 4 + c] = newValue / size;
                    }
                    dstData[(j + offsetI) * 4 + 3] = mData[offsetY + startY * mWidth * 4 + (j + startX) * 4 + 3];
                }
            }

        } else {
            console.error("不支持类型。");
        }
        return dst;
    }

    function getGaussianKernel(__n, __sigma) {
        var SMALL_GAUSSIAN_SIZE = 7,
            smallGaussianTab = [[1],
                                [0.25, 0.5, 0.25],
                                [0.0625, 0.25, 0.375, 0.25, 0.0625],
                                [0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125]
            ];

        var fixedKernel = __n & 2 == 1 && __n <= SMALL_GAUSSIAN_SIZE && __sigma <= 0 ? smallGaussianTab[__n >> 1] : 0;

        var sigmaX = __sigma > 0 ? __sigma : ((__n - 1) * 0.5 - 1) * 0.3 + 0.8,
            scale2X = -0.5 / (sigmaX * sigmaX),
            sum = 0;

        var i, x, t, kernel = [];

        for (i = 0; i < __n; i++) {
            x = i - (__n - 1) * 0.5;
            t = fixedKernel ? fixedKernel[i] : Math.exp(scale2X * x * x);
            kernel[i] = t;
            sum += t;
        }

        sum = 1 / sum;

        for (i = __n; i--;) {
            kernel[i] *= sum;
        }

        return kernel;
    };

    function GaussianBlur(__src, __size1, __size2, __sigma1, __sigma2, __borderType, __dst) {
        if (__src.type && __src.type == "CV_RGBA") {
            var height = __src.row,
                width = __src.col,
                dst = __dst || new Mat(height, width, __src.data),
                dstData = dst.data;
            var sigma1 = __sigma1 || 0,
                sigma2 = __sigma2 || __sigma1;
            var size1 = __size1 || Math.round(sigma1 * 6 + 1) | 1,
                size2 = __size2 || Math.round(sigma2 * 6 + 1) | 1,
                size = size1 * size2;
            if (size1 % 2 !== 1 || size2 % 2 !== 1) {
                console.error("size必须是奇数。");
                return __src;
            }
            var startX = Math.floor(size1 / 2),
                startY = Math.floor(size2 / 2);
            var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType),
                mData = withBorderMat.data,
                mWidth = withBorderMat.col;

            var kernel1 = getGaussianKernel(size1, sigma1),
                kernel2,
                kernel = new Array(size1 * size2);

            if (size1 === size2 && sigma1 === sigma2)
                kernel2 = kernel1;
            else
                kernel2 = getGaussianKernel(size2, sigma2);

            var i, j, c, y, x;

            for (i = kernel2.length; i--;) {
                for (j = kernel1.length; j--;) {
                    kernel[i * size1 + j] = kernel2[i] * kernel1[j];
                }
            }

            var newValue, nowX, offsetY, offsetI;

            for (i = height; i--;) {
                offsetI = i * width;
                for (j = width; j--;) {
                    for (c = 3; c--;) {
                        newValue = 0;
                        for (y = size2; y--;) {
                            offsetY = (y + i) * mWidth * 4;
                            for (x = size1; x--;) {
                                nowX = (x + j) * 4 + c;
                                newValue += (mData[offsetY + nowX] * kernel[y * size1 + x]);
                            }
                        }
                        dstData[(j + offsetI) * 4 + c] = newValue;
                    }
                    dstData[(j + offsetI) * 4 + 3] = mData[offsetY + startY * mWidth * 4 + (j + startX) * 4 + 3];
                }
            }

        } else {
            console.error("不支持的类型");
        }
        return dst;
    }

    function medianBlur(__src, __size1, __size2, __borderType, __dst) {
        if (__src.type && __src.type == "CV_RGBA") {
            var height = __src.row,
                width = __src.col,
                dst = __dst || new Mat(height, width, __src.data),
                dstData = dst.data;
            var size1 = __size1 || 3,
                size2 = __size2 || size1,
                size = size1 * size2;
            if (size1 % 2 !== 1 || size2 % 2 !== 1) {
                console.error("size必须是奇数");
                return __src;
            }
            var startX = Math.floor(size1 / 2),
                startY = Math.floor(size2 / 2);
            var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType),
                mData = withBorderMat.data,
                mWidth = withBorderMat.col;

            var newValue = [], nowX, offsetY, offsetI;
            var i, j, c, y, x;

            for (i = height; i--;) {
                offsetI = i * width;
                for (j = width; j--;) {
                    for (c = 3; c--;) {
                        for (y = size2; y--;) {
                            offsetY = (y + i) * mWidth * 4;
                            for (x = size1; x--;) {
                                nowX = (x + j) * 4 + c;
                                newValue[y * size1 + x] = mData[offsetY + nowX];
                            }
                        }
                        newValue.sort();
                        dstData[(j + offsetI) * 4 + c] = newValue[Math.round(size / 2)];
                    }
                    dstData[(j + offsetI) * 4 + 3] = mData[offsetY + startY * mWidth * 4 + (j + startX) * 4 + 3];
                }
            }

        } else {
            console.error("类型不支持");
        }
        return dst;
    };

    function copyMakeBorder(_src, _top, _left, _bottom, _right, _borderType, _value) {
        if (_src.type != "CV_RGBA") {
            console.log("not support this type");
        } else if (_borderType == "BORDER_CONSTANT") {
            return copyMakeConstBorder_8U(_src, _top, _left, _bottom, _right, _value);
        } else {
            return copyMakeBorder_8U(_src, _top, _left, _bottom, _right, _borderType);
        }
    }

    function borderInterpolate(_p, _len, _borderType) {
        if (_p < 0 || _p >= _len) {
            switch (_borderType) {
                case "BORDER_REPLICATE":
                    _p = _p < 0 ? 0 : _len - 1;
                    break;
                case "BORDER_REFLECT":
                case "BORDER_REFLECT_101":
                    var delta = (_borderType == "BORDER_REFLECT_101");
                    if (_len == 1) {
                        return 0;
                    }
                    do {
                        if (_p < 0) {
                            _p = -_p - 1 + delta;
                        } else {
                            _p = _len - 1 - (_p - _len) - delta;
                        }
                    } while (_p < 0 || _p >= _len);
                    break;
                case "BORDER_WRAP":
                    if (_p < 0) {
                        _p -= ((_p - _len + 1) / _len | 0) * _len;
                    }
                    if (_p >= _len) {
                        _p %= _len;
                    }
                    break;
                case "BORDER_CONSTANT":
                    _p = -1;
                default:
                    console.log(arguments.callee, "UNSPPORT_BORDER_TYPE");
            }
        }
        return _p;
    }

    function copyMakeBorder_8U(_src, _top, _left, _bottom, _right, _borderType) {
        var i, j;
        var width = _src.col,
            height = _src.row;
        var top = _top,
            left = _left || _top,
            right = _right || left,
            bottom = _bottom || top,
            dstWidth = width + left + right,
            dstHeight = height + top + bottom,
            borderType = _borderType || "BORDER_REFLECT";
        var buffer = new ArrayBuffer(dstHeight * dstWidth * 4),
            tab = new Uint32Array(left + right);

        for (i = 0; i < left; i++) {
            tab[i] = borderInterpolate(i - left, width, borderType);
        }
        for (i = 0; i < right; i++) {
            tab[i + left] = borderInterpolate(width + i, width, borderType);
        }

        var tempArray, data;

        for (i = 0; i < height; i++) {
            tempArray = new Uint32Array(buffer, (i + top) * dstWidth * 4, dstWidth);
            data = new Uint32Array(_src.buffer, i * width * 4, width);
            for (j = 0; j < left; j++)
                tempArray[j] = data[tab[j]];
            for (j = 0; j < right; j++)
                tempArray[j + width + left] = data[tab[j + left]];
            tempArray.set(data, left);
        }

        var allArray = new Uint32Array(buffer);
        for (i = 0; i < top; i++) {
            j = borderInterpolate(i - top, height, _borderType);
            tempArray = new Uint32Array(buffer, i * dstWidth * 4, dstWidth);
            tempArray.set(allArray.subarray((j + top) * dstWidth, (j + top + 1) * dstWidth));
        }
        for (i = 0; i < bottom; i++) {
            j = borderInterpolate(i + height, height, borderType);
            tempArray = new Uint32Array(buffer, (i + top + height) * dstWidth * 4, dstWidth);
            tempArray.set(allArray.subarray((j + top) * dstWidth, (j + top + 1) * dstWidth));
        }

        return new Mat(dstHeight, dstWidth, new Uint8ClampedArray(buffer));
    }

    function copyMakeConstBorder_8U(_src, _top, _left, _bottom, _right, _value) {
        var i, j;
        var width = _src.col,
            height = _src.row;
        var top = _top,
            left = _left || _top,
            right = _right || left,
            bottom = _bottom || top,
            dstWidth = width + left + right,
            dstHeight = height + top + bottom,
            value = _value || [0, 0, 0, 255];
        var constBuf = new ArrayBuffer(dstWidth * 4),
            constArray = new Uint8ClampedArray(constBuf);
        buffer = new ArrayBuffer(dstHeight * dstWidth * 4);

        for (i = 0; i < dstWidth; i++) {
            for (j = 0; j < 4; j++) {
                constArray[i * 4 + j] = value[j];
            }
        }

        constArray = new Uint32Array(constBuf);
        var tempArray;

        for (i = 0; i < height; i++) {
            tempArray = new Uint32Array(buffer, (i + top) * dstWidth * 4, left);
            tempArray.set(constArray.subarray(0, left));
            tempArray = new Uint32Array(buffer, ((i + top + 1) * dstWidth - right) * 4, right);
            tempArray.set(constArray.subarray(0, right));
            tempArray = new Uint32Array(buffer, ((i + top) * dstWidth + left) * 4, width);
            tempArray.set(new Uint32Array(_src.buffer, i * width * 4, width));
        }

        for (i = 0; i < top; i++) {
            tempArray = new Uint32Array(buffer, i * dstWidth * 4, dstWidth);
            tempArray.set(constArray);
        }

        for (i = 0; i < bottom; i++) {
            tempArray = new Uint32Array(buffer, (i + top + height) * dstWidth * 4, dstWidth);
            tempArray.set(constArray);
        }

        return new Mat(dstHeight, dstWidth, new Uint8ClampedArray(buffer));
    }

    window.imageBlur = imageBlur;
})();

  调用示例:

var iCanvas = document.getElementById("blur");
var imgBlur = new imageBlur(iCanvas, "images/1.jpg", "BORDER_REFLECT", "right", "blur");
imgBlur.render();

  效果(核的横向和纵向大小均取为11):

 

 

 

posted @ 2013-01-22 23:59  artwl  阅读(1126)  评论(2编辑  收藏  举报

个人简介

var ME = {
	"name": "土豆/Artwl",
	"job": "coding",
	"languages": [
		"JS", "HTML",
                "CSS", "jQuery"
		"MVC",".NET",
		"设计模式"
	],
	"hobby": [
		"阅读", "旅游",
		"音乐", "电影"
	]
}
TOP