文章目录
-
目录
前言
仅仅是记录与分享自己在学习过程中遇到的问题与解决办法,可能有一些错误的观点或理解等,如果有说错的地方麻烦请大家指正,谢谢。该代码仅仅是从rgb角度进行操作,没有使用opencv等第三方库。
一、LBP特征是什么?
LBP特征(Local Binary Pattern)
LBP含义为局部二进制模式,是用来描述图像局部特征的算子,由T.Ojala, M.Pietikäinen, 和 D. Harwood在1994年提出,最初是为纹理描述而设计的。由于LBP对单调灰度变化的不变性和计算效率高,其适用于高要求的图像分析任务,在计算机视觉的许多领域都得到了广泛的应用:比如人脸识别、目标检测、应用LBP特征来进行训练目标检测分类器。
详细内容可以去这篇博客里学习。(1条消息) 超级详细全面的LBP特征(Local Binary Pattern)讲解_literacy_wang的博客-CSDN博客https://blog.csdn.net/literacy_wang/article/details/106742890
二、大致思路
我们将LBP分为两种方法,下面介绍第一种
1.原始的LBP算法:
原始的LBP算子定义在像素3×3的邻域内,以邻域中心像素为阈值,相邻的8个像素的灰度值与邻域中心的像素值进行比较,若周围像素大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,3×3邻域内的8个点经过比较可产生8位二进制数,将这8位二进制数依次排列形成一个二进制数字,这个二进制数字就是中心像素的LBP值。由于共有八位二进制数,每位二进制数的取值为1或0,因此原始的LBP取值有2^8=256种。如下图所示:
根据所得的8个点按顺时针处理成一个十进制的值,则为中心像素的像素值。如上图0*2^7+1*2^6+1*2^5+1*2^4+1*2^3+1*2^2+0*2^1+0*2^0=124。
起始点位置的不同得到的结果也不同,但刚性需求是按顺时针处理。
代码如下:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import java.util.LinkedList;
import java.util.Deque;
public class LBP
{
BufferedImage bufferedImage;
BufferedImage buf;
int height;
int width;
Deque<Integer> queue = new LinkedList<>();
public LBP(String path)
{
try
{
bufferedImage = ImageIO.read(new File(path));
}
catch (IOException e)
{
e.printStackTrace();
}
height = bufferedImage.getHeight();
width = bufferedImage.getWidth();
buf = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
public void getTexture()
{
getWhite();
getCenterPixel();
}
public void getCenterPixel()
{
double pixel = 0;
for(int y = 1;y < height-1;y++)
{
for(int x = 1;x < width-1;x++)
{
pixel = getGray(x,y);
getAround(x,y,pixel);
}
}
}
public void getAround(int x,int y,double pixel)//从左上开始
{
Compare(x-1,y,pixel);
Compare(x-1,y-1,pixel);
Compare(x,y-1,pixel);
Compare(x+1,y-1,pixel);
Compare(x+1,y,pixel);
Compare(x+1,y+1,pixel);
Compare(x,y+1,pixel);
Compare(x-1,y+1,pixel);
Calculate(x,y);
}
public void Compare(int x,int y,double pixel)
{
double value = getGray(x,y);
if(value > pixel)
queue.offer(1);
else
queue.offer(0);
}
public void Calculate(int x,int y)
{
int pixelval = 0;
for(int i = queue.size()-1;i >= 0;i--)
{
pixelval += queue.poll()*Math.pow(2,i);
}
Color color = new Color(pixelval,pixelval,pixelval);
buf.setRGB(x,y,color.getRGB());
}
public double getGray(int x,int y)
{
Color color = new Color(bufferedImage.getRGB(x,y));
int r = color.getRed();
int g = color.getGreen();
int b = color.getBlue();
return 0.3*r + 0.59*g + 0.11*b;
}
public void getWhite()
{
Color white = new Color(255,255,255);
for(int y = 0;y < height;y++)
{
for(int x = 0;x < width;x++)
{
buf.setRGB(x,y,white.getRGB());
}
}
}
public void outPicture(String path)
{
try
{
ImageIO.write(buf,"png",new File(path));
}
catch (IOException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
LBP lbp1 = new LBP("图片/1.png");//图片是相对地址,如果想要直接用需要更换下图片路径即可
lbp1.getTexture();
lbp1.outPicture("图片/zuoshang1_1wenli.png");
}
}
在此插入几张效果图
以上是不同起始点得出的不同效果。
2.Circular LBP or Extended LBP:
基本的 LBP算子的最大缺陷在于它只覆盖了一个固定半径范围内的小区域,这显然不能满足不同尺寸和频率纹理的需要,固定的近邻区域对于尺度变化会编码失效。基本的LBP具有灰度不变性(指光照变化是否会对描述产生影响,对光照不敏感,即相对大小不变),但由于其对八位二进制顺序不硬性要求所以不具备旋转不变性(指对齐给予一个定义于内积空间的函数,对其任意旋转,函数的参数值可能改变,但数值不变)。
为了适应不同尺度的纹理特征,Ojala等对 LBP 算子进行了改进,将 3×3邻域扩展到任意邻域,并用圆形邻域代替了正方形邻域,改进后的 LBP 算子允许在半径为 R 的圆形邻域内有任意多个像素点。从而得到了诸如半径为R的圆形区域内含有P个采样点的LBP算子。
x_p = x_c+Rcos(2πp/P)
y_p = y_c−Rsin(2πp/P)
(x_c,y_c)为中心点,(x_p,y_p)为采样点。R表示邻域半径,p表示第p个采样点,P为采样数目。
详细内容可以去上面推荐的博客上看,在此就不多赘述了,直接上代码:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import java.util.LinkedList;
import java.util.Deque;
public class LBP
{
BufferedImage bufferedImage;
BufferedImage buf;
int height;
int width;
Deque<Integer> queue = new LinkedList<>();
public LBP(String path)
{
try
{
bufferedImage = ImageIO.read(new File(path));
}
catch (IOException e)
{
e.printStackTrace();
}
height = bufferedImage.getHeight();
width = bufferedImage.getWidth();
buf = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
public void getTexture()
{
getWhite();
getCenterPixel();
}
public void getCenterPixel()
{
double pixel = 0;
for(int y = 3;y < height-3;y++)//从2开始是因为半径为2
{
for(int x = 3;x < width-3;x++)
{
pixel = getGray(x,y);
getAround(x,y,pixel);
}
}
}
public void getAround(int x,int y,double pixel)//半径为3,采样点为8,半径越小,纹理越精细,采样点越多,亮度越大
{
for(int i = 1;i < 9;i++)
Compare(x+3*Math.cos(2*3.14*i/9),y-3*Math.sin(2*3.14*i/9),pixel);
Calculate(x,y);
}
public void Compare(double x,double y,double pixel)
{
double value;
int a = (int)x;
int b = (int)y;
if(x%a == 0 && y%b == 0)//判断x,y是否为整数,若为0则为整数,反之则不是
value = getGray(a,b);
else
value = getLinearInterpolation(x,y);
if(value >= pixel)
queue.offer(1);
else
queue.offer(0);
}
public double getLinearInterpolation(double x,double y)//对x,y进行双线性插值
{
int x1 = (int)x;
int x2 = x1+1;
int y1 = (int)y;
int y2 = y1+1;
double v11 = getGray(x1,y1);
double v21 = getGray(x2,y1);
double v12 = getGray(x1,y2);
double v22 = getGray(x2,y2);
double factor1 = (x2-x)*v11/(x2-x1)+(x-x1)*v21/(x2-x1);//factor1,factor2对x进行线性插值
double factor2 = (x2-x)*v12/(x2-x1)+(x-x1)*v22/(x2-x1);
return (y2-y)*factor1/(y2-y1)+(y-y1)*factor2/(y2-y1);//对y进行线性插值得值并返回
}
public void Calculate(int x,int y)
{
int pixelval = 0;
for(int i = queue.size()-1;i >= 0;i--)
{
pixelval += queue.poll()*Math.pow(2,i);
}
Color color = new Color(pixelval,pixelval,pixelval);
buf.setRGB(x,y,color.getRGB());
}
public void getWhite()
{
Color white = new Color(255,255,255);
for(int y = 0;y < height;y++)
{
for(int x = 0;x < width;x++)
{
buf.setRGB(x,y,white.getRGB());
}
}
}
public double getGray(int x,int y)
{
Color color = new Color(bufferedImage.getRGB(x,y));
int r = color.getRed();
int g = color.getGreen();
int b = color.getBlue();
return 0.3*r + 0.59*g + 0.11*b;
}
public void outPicture(String path)
{
try
{
ImageIO.write(buf,"png",new File(path));
}
catch (IOException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
LBP lbp1 = new LBP("图片/8.png");
lbp1.getTexture();
lbp1.outPicture("图片/8_1wenli.png");
}
}
同样的,上效果图:
如果有可以优化的点欢迎大家帮忙指出!看到这里了点个赞吧!感谢!