我正在尝试通过Anil Jain实现改进指纹图像的方法。作为入门者,我在提取方向图像时遇到了一些困难,并严格遵循该文章第2.4节中描述的步骤。
所以,这是输入图像:
这是在使用与该论文完全相同的方法进行标准化之后:
我期待看到类似的东西(来自互联网的一个例子):
然而,这是我显示获得的方向矩阵所得到的:
显然这是错误的,并且它还为原始输入图像中的那些零点提供非零值。
这是我写的代码:
cv::Mat orientation(cv::Mat inputImage)
{
cv::Mat orientationMat = cv::Mat::zeros(inputImage.size(), CV_8UC1);
// compute gradients at each pixel
cv::Mat grad_x, grad_y;
cv::Sobel(inputImage, grad_x, CV_16SC1, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
cv::Sobel(inputImage, grad_y, CV_16SC1, 0, 1, 3, 1, 0, cv::BORDER_DEFAULT);
cv::Mat Vx, Vy, theta, lowPassX, lowPassY;
cv::Mat lowPassX2, lowPassY2;
Vx = cv::Mat::zeros(inputImage.size(), inputImage.type());
Vx.copyTo(Vy);
Vx.copyTo(theta);
Vx.copyTo(lowPassX);
Vx.copyTo(lowPassY);
Vx.copyTo(lowPassX2);
Vx.copyTo(lowPassY2);
// estimate the local orientation of each block
int blockSize = 16;
for(int i = blockSize/2; i < inputImage.rows - blockSize/2; i+=blockSize)
{
for(int j = blockSize / 2; j < inputImage.cols - blockSize/2; j+= blockSize)
{
float sum1 = 0.0;
float sum2 = 0.0;
for ( int u = i - blockSize/2; u < i + blockSize/2; u++)
{
for( int v = j - blockSize/2; v < j+blockSize/2; v++)
{
sum1 += grad_x.at<float>(u,v) * grad_y.at<float>(u,v);
sum2 += (grad_x.at<float>(u,v)*grad_x.at<float>(u,v)) * (grad_y.at<float>(u,v)*grad_y.at<float>(u,v));
}
}
Vx.at<float>(i,j) = sum1;
Vy.at<float>(i,j) = sum2;
double calc = 0.0;
if(sum1 != 0 && sum2 != 0)
{
calc = 0.5 * atan(Vy.at<float>(i,j) / Vx.at<float>(i,j));
}
theta.at<float>(i,j) = calc;
// Perform low-pass filtering
float angle = 2 * calc;
lowPassX.at<float>(i,j) = cos(angle * pi / 180);
lowPassY.at<float>(i,j) = sin(angle * pi / 180);
float sum3 = 0.0;
float sum4 = 0.0;
for(int u = -lowPassSize / 2; u < lowPassSize / 2; u++)
{
for(int v = -lowPassSize / 2; v < lowPassSize / 2; v++)
{
sum3 += inputImage.at<float>(u,v) * lowPassX.at<float>(i - u*lowPassSize, j - v * lowPassSize);
sum4 += inputImage.at<float>(u, v) * lowPassY.at<float>(i - u*lowPassSize, j - v * lowPassSize);
}
}
lowPassX2.at<float>(i,j) = sum3;
lowPassY2.at<float>(i,j) = sum4;
float calc2 = 0.0;
if(sum3 != 0 && sum4 != 0)
{
calc2 = 0.5 * atan(lowPassY2.at<float>(i, j) / lowPassX2.at<float>(i, j)) * 180 / pi;
}
orientationMat.at<float>(i,j) = calc2;
}
}
return orientationMat;
}
我已经在网上搜索了很多,但几乎所有这些都在Matlab中。并且很少有人使用OpenCV,但他们也没有帮助我。我真诚地希望有人可以通过我的代码并指出任何错误来帮助。先感谢您。
更新
以下是我根据论文采取的步骤:
使用公式估算以像素(i,j)为中心的每个块的局部方向:
执行低通滤波以消除噪音。为此,将方向图像转换为连续矢量字段,定义为:
其中W是二维低通滤波器,w(phi)x w(phi)是其大小,等于5.
UPDATE2
这是在Sobel操作中将垫类型更改为CV_16SC1之后orientationMat的输出,如Micka建议的那样:
答案 0 :(得分:3)
也许现在回答为时已晚,但无论如何,有人可以稍后阅读并解决同样的问题。
我在同一个算法中工作了一段时间,你发布了相同的方法......但是当编写报文时我会发现一些写错误(我猜)。经过与方程式的斗争,我通过寻找其他类似的作品找到了这个错误。
这对我有用......
Vy(i, j) = 2*dx(u,v)*dy(u,v)
Vx(i,j) = dx(u,v)^2 - dy(u,v)^2
O(i,j) = 0.5*arctan(Vy(i,j)/Vx(i,j)
(对不起,我无法发布图片,所以我写了修改过的ecuations。记住“u”和“v”是BlockSize通过BlockSize窗口求和的位置)
第一件事,也是最重要的(显然)是方程式,我看到在不同的作品中,这些表达式确实不同,并且在每一个中他们都谈到了Hong等人的相同算法。 关键是找到梯度的最小均方(前3个方程)(Vx和Vy),我提供了上面的校正公式。然后你可以计算非重叠窗口的角度θ(在报纸中推荐的16x16尺寸),之后算法说你必须计算“x”和“y”方向(Phi_x和Phi_y)的加倍角度的大小。
Phi_x(i,j) = V(i,j) * cos(2*O(i,j))
Phi_y(i,j) = V(i,j) * sin(2*O(i,j))
Magnitud只是:
V = sqrt(Vx(i,j)^2 + Vy(i,j)^2)
请注意,在相关工作中并未提及您必须使用渐变幅度,但这对我来说是有意义的(对我而言)。在所有这些修正之后,您可以将低通滤波器应用于Phi_x和Phi_y,我使用了一个大小为5x5的简单掩码来平均这个幅度(类似于opencv的medianblur())。
最后一件事是计算新角度,即O(i,j)图像中25ith邻居的平均值,为此您只需:
O'(i,j)= 0.5 * arctan(Phi_y / Phi_x)
我们就在那里......所有这些只是用于计算BlockSize by BlockSize非重叠窗口中正常矢量与RIDGES方向(O'(i,j))的角度,这是什么意思?这意味着我们刚刚计算出的角度垂直于脊,简单来说我们只是计算了riges的角度加上90度......为了得到我们需要的角度,我们只需要将得到的角度减去90°。
要画线,我们需要有一个初始点(X0,Y0)和一个终点(X1,Y1)。为此设想一个以(X0,Y0)为中心的圆圈,其中有一个“r”:
x0 = i + blocksize/2
y0 = j + blocksize/2
r = blocksize/2
注意我们将i和j添加到第一个坐标,因为窗口正在移动,我们将从非重叠窗口的中心开始绘制线,因此我们不能仅使用非重叠窗口的中心。 然后要计算绘制直线的结束坐标,我们就必须使用直角三角形......
X1 = r*cos(O'(i,j)-90°)+X0
Y1 = r*sin(O'(i,j)-90°)+Y0
X2 = X0-r*cos(O'(i,j)-90°)
Y2 = Y0-r*cos(O'(i,j)-90°)
然后只使用opencv线函数,其中初始Point为(X0,Y0),最终Point为(X1,Y1)。除此之外,我绘制了16x16的窗口并计算了X1和Y1(X2和Y2)的对位点以绘制整个窗口的一条线。
希望这有助于某人。
我的结果......
答案 1 :(得分:1)
主要功能:
Mat mat = imread("nwmPa.png",0);
mat.convertTo(mat, CV_32F, 1.0/255, 0);
Normalize(mat);
int blockSize = 6;
int height = mat.rows;
int width = mat.cols;
Mat orientationMap;
orientation(mat, orientationMap, blockSize);
规格化:
void Normalize(Mat & image)
{
Scalar mean, dev;
meanStdDev(image, mean, dev);
double M = mean.val[0];
double D = dev.val[0];
for(int i(0) ; i<image.rows ; i++)
{
for(int j(0) ; j<image.cols ; j++)
{
if(image.at<float>(i,j) > M)
image.at<float>(i,j) = 100.0/255 + sqrt( 100.0/255*pow(image.at<float>(i,j)-M,2)/D );
else
image.at<float>(i,j) = 100.0/255 - sqrt( 100.0/255*pow(image.at<float>(i,j)-M,2)/D );
}
}
}
方向图:
void orientation(const Mat &inputImage, Mat &orientationMap, int blockSize)
{
Mat fprintWithDirectionsSmoo = inputImage.clone();
Mat tmp(inputImage.size(), inputImage.type());
Mat coherence(inputImage.size(), inputImage.type());
orientationMap = tmp.clone();
//Gradiants x and y
Mat grad_x, grad_y;
// Sobel(inputImage, grad_x, CV_32F, 1, 0, 3, 1, 0, BORDER_DEFAULT);
// Sobel(inputImage, grad_y, CV_32F, 0, 1, 3, 1, 0, BORDER_DEFAULT);
Scharr(inputImage, grad_x, CV_32F, 1, 0, 1, 0);
Scharr(inputImage, grad_y, CV_32F, 0, 1, 1, 0);
//Vector vield
Mat Fx(inputImage.size(), inputImage.type()),
Fy(inputImage.size(), inputImage.type()),
Fx_gauss,
Fy_gauss;
Mat smoothed(inputImage.size(), inputImage.type());
// Local orientation for each block
int width = inputImage.cols;
int height = inputImage.rows;
int blockH;
int blockW;
//select block
for(int i = 0; i < height; i+=blockSize)
{
for(int j = 0; j < width; j+=blockSize)
{
float Gsx = 0.0;
float Gsy = 0.0;
float Gxx = 0.0;
float Gyy = 0.0;
//for check bounds of img
blockH = ((height-i)<blockSize)?(height-i):blockSize;
blockW = ((width-j)<blockSize)?(width-j):blockSize;
//average at block WхW
for ( int u = i ; u < i + blockH; u++)
{
for( int v = j ; v < j + blockW ; v++)
{
Gsx += (grad_x.at<float>(u,v)*grad_x.at<float>(u,v)) - (grad_y.at<float>(u,v)*grad_y.at<float>(u,v));
Gsy += 2*grad_x.at<float>(u,v) * grad_y.at<float>(u,v);
Gxx += grad_x.at<float>(u,v)*grad_x.at<float>(u,v);
Gyy += grad_y.at<float>(u,v)*grad_y.at<float>(u,v);
}
}
float coh = sqrt(pow(Gsx,2) + pow(Gsy,2)) / (Gxx + Gyy);
//smoothed
float fi = 0.5*fastAtan2(Gsy, Gsx)*CV_PI/180;
Fx.at<float>(i,j) = cos(2*fi);
Fy.at<float>(i,j) = sin(2*fi);
//fill blocks
for ( int u = i ; u < i + blockH; u++)
{
for( int v = j ; v < j + blockW ; v++)
{
orientationMap.at<float>(u,v) = fi;
Fx.at<float>(u,v) = Fx.at<float>(i,j);
Fy.at<float>(u,v) = Fy.at<float>(i,j);
coherence.at<float>(u,v) = (coh<0.85)?1:0;
}
}
}
} ///for
GaussConvolveWithStep(Fx, Fx_gauss, 5, blockSize);
GaussConvolveWithStep(Fy, Fy_gauss, 5, blockSize);
for(int m = 0; m < height; m++)
{
for(int n = 0; n < width; n++)
{
smoothed.at<float>(m,n) = 0.5*fastAtan2(Fy_gauss.at<float>(m,n), Fx_gauss.at<float>(m,n))*CV_PI/180;
if((m%blockSize)==0 && (n%blockSize)==0){
int x = n;
int y = m;
int ln = sqrt(2*pow(blockSize,2))/2;
float dx = ln*cos( smoothed.at<float>(m,n) - CV_PI/2);
float dy = ln*sin( smoothed.at<float>(m,n) - CV_PI/2);
arrowedLine(fprintWithDirectionsSmoo, Point(x, y+blockH), Point(x + dx, y + blockW + dy), Scalar::all(255), 1, CV_AA, 0, 0.06*blockSize);
// qDebug () << Fx_gauss.at<float>(m,n) << Fy_gauss.at<float>(m,n) << smoothed.at<float>(m,n);
// imshow("Orientation", fprintWithDirectionsSmoo);
// waitKey(0);
}
}
}///for2
normalize(orientationMap, orientationMap,0,1,NORM_MINMAX);
imshow("Orientation field", orientationMap);
orientationMap = smoothed.clone();
normalize(smoothed, smoothed, 0, 1, NORM_MINMAX);
imshow("Smoothed orientation field", smoothed);
imshow("Coherence", coherence);
imshow("Orientation", fprintWithDirectionsSmoo);
}
似乎没有忘记)
答案 2 :(得分:0)
我已经仔细阅读了您的代码,发现您在计算 sum3 和 sum4 时出错:
sum3 += inputImage.at<float>(u,v) * lowPassX.at<float>(i - u*lowPassSize, j - v * lowPassSize);
sum4 += inputImage.at<float>(u, v) * lowPassY.at<float>(i - u*lowPassSize, j - v * lowPassSize);
而不是 inputImage ,您应该使用低通滤波器。