糯米文學吧

位置:首頁 > 設計 > 網頁設計

使用JavaScript進行基本圖形操作與處理

前言

使用JavaScript進行基本圖形操作與處理

上一篇文章,我們講解了圖像的虛擬邊緣,這篇文章開始進行平滑(也就是模糊)處理。

基本原理

這裏直接引用OpenCV 2.4+ C++ 平滑處理和OpenCV 2.4+ C++ 邊緣梯度計算的相關內容:

平滑也稱模糊, 是一項簡單且使用頻率很高的圖像處理方法。

平滑處理時需要用到一個濾波器

。 最常用的濾波器是線性濾波器,線性濾波處理的輸出像素值(例如:

)是輸入像素值(例如:

)的加權平均:

稱為核

, 它僅僅是一個加權係數。

這裏涉及一種叫做“卷積”的運算,那麼卷積是什麼呢?

卷積是在每一個圖像塊與某個算子(核)之間進行的運算。

核?!

nbsp;

dsds

核就是一個固定大小的數值數組。該數組帶有一個錨點

,一般位於數組中央。

可是這怎麼運算啊?

假如你想得到圖像的某個特定位置的卷積值,可用下列方法計算:

將核的錨點放在該特定位置的像素上,同時,核內的其他值與該像素鄰域的各像素重合;將核內各值與相應像素值相乘,並將乘積相加;將所得結果放到與錨點對應的像素上;對圖像所有像素重複上述過程。

公式表示上述過程如下:

在圖像邊緣的卷積怎麼辦呢?

計算卷積前,需要通過複製源圖像的邊界創建虛擬像素,這樣邊緣的地方也有足夠像素計算卷積了。這就是為什麼上一篇文章需要做虛擬邊緣函數。

均值平滑

均值平滑實際上就是內核元素全是1的卷積運算,然後再除以內核的.大小,用數學表達式來表示就是:

下面我們來實現均值平滑函數blur:

複製代碼 代碼如下:

function blur(__src, __size1, __size2, __borderType, __dst){

if(__ && __ == "CV_RGBA"){

var height = __,

width = __,

dst = __dst || new Mat(height, width, CV_RGBA),

dstData = ;

var size1 = __size1 || 3,

size2 = __size2 || size1,

size = size1 * size2;

if(size1 % 2 !== 1 || size2 % 2 !== 1){

r("size大小必須是奇數");

return __src;

}

var startX = r(size1 / 2),

startY = r(size2 / 2);

var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType),

mData = ,

mWidth = ;

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{

r("不支持類型。");

}

return dst;

}

其中size1和size2分別是核的橫向和縱向大小,並且必須是正奇數。

高斯平滑

最有用的濾波器 (儘管不是最快的)。 高斯濾波是將輸入數組的每一個像素點與高斯內核

卷積將卷積和當作輸出像素值。

參考一維高斯函數,我們可以看見,他是個中間大兩邊小的函數。

所以高斯濾波器其加權數是中間大,四周小的。

其二維高斯函數為:

其中

為均值 (峯值對應位置),

代表標準差 (變量

和 變量

各有一個均值,也各有一個標準差)。

這裏參考OpenCV的實現,不過應該還有優化空間,因為還沒用到分離濾波器。

首先我們做一個getGaussianKernel來返回高斯濾波器的一維數組。

複製代碼 代碼如下:

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="" __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] : (scale2X * x * x);

kernel[i] = t;

sum += t;

}

sum = 1 / sum;

for(i = __n; i--;){

kernel[i] *= sum;

}

return kernel;

};

然後通過兩個這個一維數組,便可以計算出一個完整的高斯內核,再利用blur裏面用到的循環方法,就可以算出高斯平滑後的矩陣了。

複製代碼 代碼如下:

function GaussianBlur(__src, __size1, __size2, __sigma1, __sigma2, __borderType, __dst){

if(__ && __ == "CV_RGBA"){

var height = __,

width = __,

dst = __dst || new Mat(height, width, CV_RGBA),

dstData = ;

var sigma1 = __sigma1 || 0,

sigma2 = __sigma2 || __sigma1;

var size1 = __size1 || d(sigma1 * 6 + 1) | 1,

size2 = __size2 || d(sigma2 * 6 + 1) | 1,

size = size1 * size2;

if(size1 % 2 !== 1 || size2 % 2 !== 1){

r("size必須是奇數。");

return __src;

}

var startX = r(size1 / 2),

startY = r(size2 / 2);

var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType),

mData = ,

mWidth = ;

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 = th; i--;){

for(j = th; 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{

r("不支持的類型");

}

return dst;

}

中值平滑

中值濾波將圖像的每個像素用鄰域 (以當前像素為中心的正方形區域)像素的

中值代替 。

依然使用blur裏面用到的循環,只要得到核中的所有值,再通過sort排序便可以得到中值,然後錨點由該值替代。

複製代碼 代碼如下:

function medianBlur(__src, __size1, __size2, __borderType, __dst){

if(__ && __ == "CV_RGBA"){

var height = __,

width = __,

dst = __dst || new Mat(height, width, CV_RGBA),

dstData = ;

var size1 = __size1 || 3,

size2 = __size2 || size1,

size = size1 * size2;

if(size1 % 2 !== 1 || size2 % 2 !== 1){

r("size必須是奇數");

return __src;

}

var startX = r(size1 / 2),

startY = r(size2 / 2);

var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType),

mData = ,

mWidth = ;

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];

}

}

();

dstData[(j + offsetI) * 4 + c] = newValue[d(size / 2)];

}

dstData[(j + offsetI) * 4 + 3] = mData[offsetY + startY * mWidth * 4 + (j + startX) * 4 + 3];

}

}

}else{

r("類型不支持");

}

return dst;

};