ホームページ >ウェブフロントエンド >jsチュートリアル >Javascript画像処理—平滑化処理の実装原理_JavaScriptスキル

Javascript画像処理—平滑化処理の実装原理_JavaScriptスキル

WBOY
WBOYオリジナル
2016-05-16 17:45:071602ブラウズ

はしがき

前回の記事では、画像の仮想エッジについて説明しました。この記事では、スムージング (つまり、ぼかし) 処理から始めます。

基本原則

ここでは、OpenCV 2.4 C のスムージング処理と OpenCV 2.4 C のエッジ勾配計算の関連コンテンツへの直接参照を示します。

スムージングは​​ぼかしとも呼ばれ、シンプルで頻繁に使用される画像処理方法です。

平滑化にはフィルタ

が必要です。 最も一般的に使用されるフィルターは線形フィルターです。

フィルター、線形フィルター プロセスの出力ピクセル値 (例: g(i,j)) は、入力ピクセル値 (例: f(i) の加重平均です。

g(i,j) = sum_{k,l} f(i k, j l) h(k,l)

h(k,l) はカーネル

と呼ばれ、単なる重み付け係数です。

これには「畳み込み」と呼ばれる演算が含まれますが、畳み込みとは何ですか?

コンボリューションは、各画像ブロックと特定の演算子 (カーネル) の間で実行される演算です。

核? !

nbsp;
DSDS

カーネルは固定サイズの数値配列です。配列にはアンカー ポイント

があり、通常は配列の中心に位置します。

kernel example

しかし、これはどのように機能するのでしょうか?

画像の特定の位置のコンボリューション値を取得したい場合は、次の方法で計算できます:

    カーネルのアンカー ポイントをピクセルのその特定の位置に配置し、同時にカーネル内の他の値がピクセルの近傍の各ピクセルと一致するように、カーネル内の各値を乗算します。対応するピクセル値を加算し、結果をアンカー ポイントに対応するピクセルに配置します。画像内のすべてのピクセルに対して上記のプロセスを繰り返します。

上記の処理を式で表すと次のようになります。

H(x,y) = sum_{i=0}^{M_{i} - 1} sum_{j=0}^{M_{j}-1} I(x i - a_{i}, y   j - a_{j})K(i,j)

画像の端の畳み込みはどうでしょうか?

畳み込みを計算する前に、ソース イメージの境界をコピーして仮想ピクセルを作成し、エッジに畳み込みを計算するのに十分なピクセルがあるようにする必要があります。前回の記事で仮想エッジ機能が必要になったのはこのためです。

平均平滑化

平均平滑化は実際には、カーネル要素がすべて 1 であり、それをカーネルのサイズで割る畳み込み演算です。数式は次のとおりです。

texttt{K} = frac{1}{texttt{ksize.width*ksize.height}} begin{bmatrix} 1 & 1 & 1 & cdots & 1 & 1 \ 1 & 1 & 1 & cdots & 1 & 1 \ hdotsfor{6} \ 1 & 1 & 1 & cdots & 1 & 1 \ end{bmatrix}

平均平滑化関数ブラーを実装しましょう:

コードをコピーします コードは次のとおりです:

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(高さ, 幅, CV_RGBA),
var size1 = __size1 ||
size2 = __size2 | | size1,
size = size1 * size2;
if(size1 % 2 !== 1 || size2 % 2 !== 1){
console.error("サイズは奇数である必要があります" );
return __src;
}
var startX = Math.floor(size1 / 2),
var withBorderMat = copyMakeBorder(__src) 、startY、startX、0、0、__borderType)、
mData = withBorderMat.data、
mWidth = withBorderMat.col;

var newValue、nowX、offsetY、offsetI; , 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; offsetI) * 4 c] = newValue / size;
}
dstData[(j offsetI) * 4 3] = mData[offsetY startY * mWidth * 4 (j startX) ) * 4 3];
}
}

}else{
console.error("サポートされていないタイプです。 ");
}
dst を返す;
}


ここで、size1 と size2 はそれぞれコアの水平サイズと垂直サイズであり、正の奇数である必要があります。

ガウス平滑化

最も便利なフィルター (最速ではありませんが)。 ガウス フィルター処理は、入力配列の各ピクセルをガウス カーネル

で畳み込み、その畳み込みの合計を出力ピクセル値として扱います。

http://www.cnblogs.com/http://www.cnblogs.com/_images/Smoothing_Tutorial_theory_gaussian_0.jpg

一次元ガウス関数を参照すると、中央が大きく両側が小さい関数であることがわかります。

つまり、ガウスフィルターの重み付け数値は中央が大きく、その周囲が小さくなります。

2 次元ガウス関数は次のとおりです:

G_{0}(x, y) = A e^{ dfrac{ -(x - mu_{x})^{2} }{ 2sigma^{2}_{x} }   dfrac{ -(y - mu_{y})^{2} }{ 2sigma^{2}_{y} } }

mu は平均 (ピーク値に対応する位置)、

sigma は標準偏差 (変数) を表します

x と変数

y それぞれに平均値があり、それぞれに標準偏差があります)。

ここでは OpenCV の実装を参照していますが、まだ分離フィルターが使用されていないため、最適化の余地があるはずです。

まず、ガウス フィルターの 1 次元配列を返す 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, 10937 5,0.21875, 0.28125, 0.21875, 0.109375, 0.03125]
];

varfixedKernel = __n & 2 == 1 && __n <= SMALL_GAUSSIAN_SIZE && __sigma & gt; 1] : 0;

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

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

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

合計 = 1 / 合計;

for(i = __n; i--;){
カーネル [i] *= 合計; >}

return kernel;
};


次に、2 つの 1 次元配列を通じて完全なガウス カーネルを計算し、blur で使用されるループ メソッドを使用します。 , ガウス平滑化行列を計算できます。


コードをコピー コードは次のとおりです:

function GaussianBlur(__src, __size1, __size2, __sigma1, __sigma2, __borderType, __dst){
if(__src.type && __src.type == "CV_RGBA"){
var height = __src .row、
幅 = __src.col、
dst = __dst || new Mat(高さ, 幅, CV_RGBA),
dstData = dst.data;
var sigma1 = __sigma1 || 0、
シグマ2 = __シグマ2 || __シグマ1;
var size1 = __size1 || Math.round(sigma1 * 6 1) | 1、
サイズ2 = __size2 || Math.round(sigma2 * 6 1) | 1、
サイズ = サイズ 1 * サイズ 2;
if(size1 % 2 !== 1 || size2 % 2 !== 1){
console.error("size は奇数でなければなりません。");
__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] * カーネル1[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("不サポートな種類");
}
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(高さ, 幅, CV_RGBA),
dstData = dst.data;
var size1 = __size1 || 3、
size2 = __size2 ||サイズ 1、
サイズ = サイズ 1 * サイズ 2;
if(size1 % 2 !== 1 || size2 % 2 !== 1){
console.error("size 必须是奇数");
__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("类型不サポート");
}
dst を返します。
};




声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。