数字图像处理之LBP特征[Java]

 文章目录


前言

仅仅是记录与分享自己在学习过程中遇到的问题与解决办法,可能有一些错误的观点或理解等,如果有说错的地方麻烦请大家指正,谢谢。该代码仅仅是从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");
	}
}

同样的,上效果图:

 

如果有可以优化的点欢迎大家帮忙指出!看到这里了点个赞吧!感谢! 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值