머리말
이전 글에서는 이미지 처리에서의 확장 및 침식 기능에 대해 설명했습니다. 이번 글에서는 가장자리 기울기 계산 기능을 수행하겠습니다.
이미지 가장자리
이미지의 가장자리는 수학적으로 어떻게 표현되나요?
![How intensity changes in an edge](http://files.jb51.net/file_images/article/201301/201301140902112.jpg)
이미지 가장자리에서 인접한 픽셀 값이 크게 변경되어야 합니다. 수학에서 도함수는 변화의 속도를 표현하는 방법입니다. 그라데이션 값의 큰 변화는 이미지 내용의 중요한 변화를 나타냅니다.
더 생생한 이미지로 설명하기 위해 1차원 그래프가 있다고 가정하겠습니다. 아래 이미지에서 회색 값의 "점프"는 가장자리가 있음을 나타냅니다.
![Intensity Plot for an edge](http://files.jb51.net/file_images/article/201301/201301140902113.jpg)
1차 미분 파생을 사용하면 가장자리 "점프"(여기서는 높은 피크로 표시됨)의 존재를 더 명확하게 확인할 수 있습니다.
![First derivative of Intensity - Plot for an edge](http://files.jb51.net/file_images/article/201301/201301140902114.jpg)
이를 통해 이웃보다 그라데이션 값이 큰 픽셀을 찾아 가장자리를 찾을 수 있다는 결론을 내릴 수 있습니다.
대략적인 그라데이션
예를 들어 코어가 3개인 경우.
먼저 x 방향의 대략적인 도함수를 계산합니다.
![G_{x} = begin{bmatrix}-1 & 0 & 1 \-2 & 0 & 2 \-1 & 0 & 1end{bmatrix} * I](http://files.jb51.net/file_images/article/201301/201301140902115.png)
그런 다음 y 방향에 대한 대략적인 도함수를 계산합니다.
![G_{y} = begin{bmatrix}-1 & -2 & -1 </P> & 0 & 0 \ 1 & 2 & 1end{bmatrix} * I](http://files.jb51.net/file_images/article/201301/201301140902116.png)
그런 다음 기울기를 계산합니다.
![G = sqrt{ G_{x}^{2} G_{y}^{2} }](http://files.jb51.net/file_images/article/201301/201301140902117.png)
물론 다음과 같이 쓸 수도 있습니다.
![G = |G_{x}| |G_{y}|](http://files.jb51.net/file_images/article/201301/201301140902118.png)
함수 구현
코드는 다음과 같습니다.
var Sobel = function(__src, __xorder, __yorder, __size, __borderType, __dst){
(__src && (__xorder ^ __yorder)) || error(arguments.callee, IS_UNDEFINED_OR_NULL/* { line} * /)
if(__src.type && __src.type === "CV_GRAY"){
var kernel1,
kernel2,
height = __src.row,
width = __src.col,
dst = __dst || new Mat(height, width, CV_16I, 1),
dstData = dst.data
size = __size || 3; {
사례 1:
크기 = 3;
사례 3:
if(__xorder){
커널 = [-1, 0, 1,
-2, 0, 2 ,
-1, 0, 1
];
}else if(__yorder){
kernel = [-1, -2, -1,
, 0, 0,
, 2 , 1
];
}
break;
사례 5:
if(__xorder){
커널 = [-1, -2, 0, 2, 1 ,
-4, -8, 0, 8, 4,
-6,-12, 0,12, 6,
-4, -8, 0, 8, 4,
- 1, - 2, 0, 2, 1
]
}else if(__yorder){
커널 = [-1, -4, -6, -4, -1,
- 2, - 8,-12, -8, -2,
, 0, 0, 0, 0,
, 8, 12, 8, 2,
, 4, 6, 4, 1
] ;
}
break;
기본값:
error(arguments.callee, UNSPPORT_SIZE/* {line} */)
GRAY216IC1Filter(__src , 크기, 높이, 너비, 커널, dstData, __borderType);
}else{
error(arguments.callee, UNSPPORT_DATA_TYPE/* {line} */)
}
return dst;
};
여기에는 커널 크기가 3과 5인 Sobel 연산자만 제공됩니다. 주된 이유는 7 이상의 커널에 대한 계산이 상대적으로 느리기 때문입니다.
단일 채널 16비트 부호 있는 정수 행렬을 출력합니다.
코드 복사 코드는 다음과 같습니다.
function GRAY216IC1Filter(__src, size, height, width, kernel, dstData, __borderType){
var start = size >>
var withBorderMat = copyMakeBorder(__src, 시작, 시작, 0, 0, __borderType);
var mData = withBorderMat.data,
mWidth = withBorderMat.col
var i, j, y, x, c;
var newValue, nowX, offsetY, offsetI;
for(i = 높이; i--;){
offsetI = i * 너비
for(j = 너비; j- -;){
newValue = 0;
for(y = 크기; y--;){
offsetY = (y i) * mWidth; ){
nowX = x j;
newValue = (mData[offsetY nowX] * kernel[y * size x])
}
}
dstData[j offsetI] =
}
}
}
그런 다음 이 필터에 커널과 행렬을 주면 괜찮습니다.
이 필터를 독립적으로 만드는 이유는 Laplacian 및 Scharr 연산자와 같은 다른 유사한 계산 Edge 함수에 사용할 수 있기 때문입니다. 부호 없는 8비트 정수로 변환
소벨 연산자는 16비트 부호 있는 정수를 계산하므로 그림으로 표시할 수 없으므로 이를 부호 없는 8비트 정수 행렬로 변환하는 함수가 필요합니다.
convertScaleAbs 함수는 각 요소의 절대값을 가져와 Int8Array 배열에 넣습니다. 255보다 큰 숫자는 할당 중에 자동으로 255로 변환되고, 0보다 작은 숫자는 자동으로 0으로 변환됩니다. 그래서 우리는 이 작업을 처리하기 위한 함수를 만들 필요가 없습니다.
function ConvertScaleAbs(__src, __dst){
__src || error(arguments.callee, IS_UNDEFINED_OR_NULL/* {line} */);
var height = __src.row,
width = __src.col,
channel = __src.channel,
sData = __src.data;
if(!__dst){
if(채널 === 1)
dst = new Mat(높이, 너비, CV_GRAY)
else if(채널 === 4)
dst = new Mat(높이, 너비, CV_RGBA);
else
dst = new Mat(높이, 너비, CV_8I, 채널)
}else{
dst = __dst;
}
var dData = dst.data;
var i, j, c; --; ){
for(j = 너비 * 채널; j--;){
dData[i * 너비 * 채널 j] = Math.abs(sData[i * 너비 * 채널 j]);
}
}
return dst;
}
비례적으로 값 병합
또한 x 방향 기울기 계산 값과 y 방향 기울기 계산 값을 중첩하는 기능이 필요합니다.
코드 복사
코드는 다음과 같습니다.var addWeighted = function(__src1, __alpha, __src2, __beta , __gamma, __dst){
(__src1 && __src2) || error(arguments.callee, IS_UNDEFINED_OR_NULL/* {line} */) var height = __src1.row,
width = __src1.col ,
alpha = __alpha || 0,
beta = __beta || 0,
channel = __src1.channel,
if(height ! == __src2 .row || 너비 !== __src2.col || 채널 !== __src2.channel){
error(arguments.callee, "Src2는 src1과 크기 및 채널 번호가 동일해야 합니다!"/* {line} */);
return null;
}
if(!__dst){
if(__src1.type.match(/CV_d /))
dst = new Mat( 높이, 너비, __src1.깊이(), 채널);
else
dst = new Mat(높이, 너비, __src1.깊이())
}else{
dst = __dst;
}
var dData = dst.data,
s1Data = __src1.data,
s2Data = __src2.data;
var i; 🎜>for (i = 높이 * 너비 * 채널; i--;)
dData[i] = __alpha * s1Data[i] __beta * s2Data[i] gamma;
return dst; >};
이 함수는 실제로 두 행렬의 해당 요소를 고정된 비율로 추가합니다.
렌더링