はじめに 前の記事では、
行列にいくつかの一般的なメソッドを追加します。この記事では、画像の仮想エッジについて説明します。
仮想エッジ 仮想エッジは、特定のマッピング関係に従って画像にエッジを追加することです。
では、仮想エッジはどのような用途に使われるのでしょうか?たとえば、反射効果を簡単に作成できます。
もちろん、これは単なる副作用です。仮想エッジは主に画像の畳み込み操作 (スムージング操作など) で使用されます。畳み込み操作では、画像を拡大することによってのみ隅を畳み込むことができます。このとき、画像を前処理して仮想エッジを追加する必要があります。
端的に言えば、画像処理の前に前処理を行うことです。
エッジタイプ OpenCV 関連ドキュメントのエッジの説明は次のとおりです:
/*
さまざまな境界線の種類、画像の境界は '|' で示されます
* BORDER_REPLICATE: aaaaaa|abcdefgh|hhhahah
* BORDER_REFLECT: abcdefgh|hgfedcb
* BORDER_REFLECT_101: gfedcb|abcdefgh|gfedcba
* BORDER_WRAP: cdefgh|abcdefgh|abcdefg
* BORDER_CONSTANT: iiiiii|abcdefgh|iiiiiii およびいくつかの指定された 'i'
* /
たとえば、BODER_REFLECT はピクセルの特定の行または列に対するものです。
abcdefgh
左側の仮想エッジは fedcba に対応し、右側は hgfedcb (反射マッピング) に対応します。 。上の図は、BORDER_REFLECT タイプの仮想エッジを図の下部に追加することによって得られます。
BORDER_CONSTANT は、すべてのエッジが固定値 i を持つことを意味します。
実装
BORDER_CONSTANT は特殊なため、他の型とは分けて扱われます。
function copyMakeBorder(__src, __top, __left, __bottom, __right , __borderType, __value){
if(__src.type != "CV_RGBA"){
console.error("サポートされていないタイプ!");
if(__borderType = == CV_BORDER_CONSTANT ){
return copyMakeConstBorder_8U(__src, __top, __left, __bottom, __right, __value)
}else{
return copyMakeBorder_8U(__src, __top, __left, __bottom, __right, __borderType); 🎜>}
};
この関数は、入力行列 src、各方向に追加されるピクセル サイズ上、左、下、右、エッジ タイプ borderType、および配列を受け入れます。値、つまり定数エッジの場合は定数値が加算されます。
次に、エッジ マッピング関係関数 borderInterpolate を導入します。
function borderInterpolate(__p, __len, __borderType) {
if(__p = __len){
switch(__borderType){
case CV_BORDER_REPLICATE:
__p = __p break ;
case CV_BORDER_REFLECT:
case CV_BORDER_REFLECT_101;
if(__len == 1)
do{
if( __p < 0)
__p = -__p - 1 デルタ;
else
__p = __len - 1 - (__p - __len) - デルタ; || __p >= __len)
break;
case CV_BORDER_WRAP:
if(__p < 0)
__p -= ((__p - __len 1) / __len) >if( __p >= __len)
__p %= __len;
case CV_BORDER_CONSTANT:
__p = -1; デフォルト:
error(arguments.callee, UNSPPORT_BORDER_TYPE/* {line} */);
}
}
return __p;
この関数の意味は、特定の行または元の長さが len であるライン。仮想ピクセル p の列 (p は通常、負の数、または行の元の長さ以上の数です。負の数はラインの左側のピクセルを表します)。元の長さ以上の数値は、右側のピクセルを表し、どのピクセルがこの行にマップされるかを表します。 CV_BORDER_REPLICATE を分析してみましょう:
__p = __p < 0 : __len - 1; つまり、p が負の数 (つまり、左側) の場合、マッピングされます。それ以外の場合は len - 1 にマッピングされます。
次に、copyMakeBorder_8U 関数を実装します。
コードをコピー
コードは次のとおりです。
関数 copyMakeBorder_8U(__src, __top, __left, __bottom, __right, __borderType){
var i, j;
var width = __src.col、
height = __src.row;
var top = __top、
left = __left || __上、
右 = __右 ||左、
下 = __下 ||上、
dstWidth = 左右の幅、
dstHeight = 上下の高さ、
borderType = borderType || CV_BORDER_REFLECT;
varbuffer = new ArrayBuffer(dstHeight * dstWidth * 4)、
tab = new Uint32Array(left right);
for(i = 0; i tab[i] = borderInterpolate(i - left, width, __borderType);
}
for(i = 0; i tab[i left] = borderInterpolate(width i, width, __borderType);
}
var tempArray、データ;
for(i = 0; i tempArray = new Uint32Array(buffer, (i top) * dstWidth * 4, dstWidth);
data = new Uint32Array(__src.buffer, i * width * 4, width);
for(j = 0; j tempArray[j] = data[tab[j]];
for(j = 0; j tempArray[j width left] = data[tab[j left]];
tempArray.set(データ、左);
}
var allArray = new Uint32Array(buffer);
for(i = 0; i j = borderInterpolate(i - top, height, __borderType);
tempArray = new Uint32Array(buffer, i * dstWidth * 4, dstWidth);
tempArray.set(allArray.subarray((j 上) * dstWidth, (j 上 1) * dstWidth));
}
for(i = 0; i j = borderInterpolate(i height, height, __borderType);
tempArray = new Uint32Array(buffer, (i 上部の高さ) * dstWidth * 4, dstWidth);
tempArray.set(allArray.subarray((j 上) * dstWidth, (j 上 1) * dstWidth));
}
return new Mat(dstHeight, dstWidth, new Uint8ClampedArray(buffer));
}
ここで説明する必要があるのは、図に示すように、最初に各実行の左右に展開が行われ、次にこのベースで上下に展開が行われることです。
その後、ArrayBuffer の特性に従って、データを記号なしの 32 ビット整数に変換して操作します。これにより、各操作単位が各画素点に対応します。どういうことですか?
は、特定の画素に相当します。ポイント:RGBA、あるチャネルが無記号8で保存されているため、上の 1 つの画素ポイントは 32 ビットの保存サイズに対応しており、ArrayBuffer の特性により、データを任意のタイプに変換して処理できます。 Uint32Array 型に変換することで、データを各画素点のデータ群に変換することができます。
コピー🎜> 代码如下: function copyMakeConstBorder_8U(__src, __top, __left, __bottom, __right, __value){
var i, j;
var width = __src.col、
height = __src.row;
var top = __top、
left = __left || __上、
右 = __右 ||左、
下 = __下 ||上、
dstWidth = 左右の幅、
dstHeight = 上下の高さ、
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 for( j = 0; j constArray[i * 4 j] = value[j];
}
}
constArray = new Uint32Array(constBuf);
var tempArray;
for(i = 0; 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 tempArray = new Uint32Array(buffer, i * dstWidth * 4, dstWidth);
tempArray.set(constArray);
}
for(i = 0; i tempArray = new Uint32Array(buffer, (i 上部の高さ) * dstWidth * 4, dstWidth);
tempArray.set(constArray);
}
return new Mat(dstHeight, dstWidth, new Uint8ClampedArray(buffer));
}
效果图
CV_BORDER_REPLICATE
CV_BORDER_REFLECT
CV_BORDER_WRAP
CV_BORDER_CONSTANT