图像识别,完成搜图功能

给定一张图片,如何从一组图片中找到与它最相近的图片呢?相信很多小伙伴跟我一样,第一想到的解决办法就是遍历比较每张图片的像素点,找到差异最小的那张图片。这种方法虽然可行,但时间复杂度高,只适用于像素点较少的图片,对于像素点较多的图片,我们需要另寻他路,即通过获取图片指纹,计算两张图片编码数的汉明距离,从而找出最相近的图片。

步骤一:将图片转为int类型的二维数组


	public int[][] getpixelarray(String path){
		BufferedImage buffimg=null;
		try {
			buffimg=ImageIO.read(new File(path));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		int w=buffimg.getWidth();
		int h=buffimg.getHeight();
		int[][] imgarr=new int[w][h];
		for(int i=0;i<w;i++) {
			for(int j=0;j<h;j++) {
				imgarr[i][j]=buffimg.getRGB(i, j);
			}
		}
		return imgarr;
	}

步骤二:缩小图片,并将其转为灰度图,保存均值化灰度值

若我们将图片二维数组中的每个像素值都取相应的二进制码字符,则会得到很长的字符串,不利于计算汉明距离,所以我们需要将图片缩少,减小所得字符串长度。下面代码是将图片矩阵缩小为同等规模的8*8矩阵。将图片缩小后,我们还需要将图片转为灰度图。那如何转成灰度图呢?假设原先图片矩阵为128*128的矩阵,则我们需要将原先图片中每一个16*16的小矩阵中的256个像素点灰度值的平均值保存至8*8的矩阵中。所以当我们在原先图片矩阵中每隔16个长度取一个16*16的小矩阵时,我们都需将灰度值初始化为0,然后遍历16*16矩阵中的每个像素点的灰度值进行求和,最后取平均值并依次将其保存至8*8的矩阵中,从而得到8*8的灰度图。

    //将图片缩小 获取8*8的灰度矩阵
		int sw=imgarr.length/8;
		int sh=imgarr[0].length/8;
		int[][] grayarr=new int[8][8];//定义一个8*8的灰度矩阵
		//计算均值化灰度值 保存至灰度矩阵中
		for(int i=0;i<=imgarr.length-sw;i+=sw) {
			for(int j=0;j<=imgarr[i].length-sh;j+=sh) {
				int grayend=0;//在原先图片矩阵中每取一个sw*sh的矩阵都将灰度值初始化为0
                //遍历计算每次所取的sw*sh矩阵中的每个像素点的灰度值 进行求和并最终取平均值
				for(int k=0;k<sw;k++) {
					for(int l=0;l<sh;l++) {
						int rgb=imgarr[i+k][j+l];
						Color color=new Color(rgb);
						//计算灰度值
						int red=color.getRed();
						int green=color.getGreen();
						int blue=color.getBlue();
						int gray=(red+green+blue)/3;
						grayend+=gray;
					}
				}
				grayarr[i/sw][j/sh]=grayend/(sw*sh);//将均值化的灰度值保存至灰度矩阵中
			}
		}

步骤三:根据均值化灰度矩阵,获取图片相应的编码数

先遍历均值化灰度矩阵中的每个灰度值,进行求和并取平均值。然后再次遍历均值化灰度矩阵,若灰度值大于所求的平均值,则将其所对应的二进制码字符设置为‘0’,否则则将其所对应的二进制码字符设置为‘1’,从而获取图片相应的二进制码字符串。

int avergray=0;
//遍历灰度矩阵 计算灰度值的平均值
for(int i=0;i<grayarr.length;i++) {
	for(int j=0;j<grayarr[i].length;j++) {
		avergray+=grayarr[i][j];
	}
}
avergray=avergray/(grayarr.length*grayarr[0].length);
//再次遍历灰度矩阵 大于平均值则设置为0
for(int k=0;k<grayarr.length;k++) {
	for(int l=0;l<grayarr[k].length;l++) {
		int gray=grayarr[k][l];
		if(gray>avergray) {
			qrcodestr+="0";
		}else {
			qrcodestr+="1";
		}
	}
}

 步骤四:计算两个字符串的汉明距离

第一种方法:计算字符串a变成字符串b需要改变的最少字符数。该最少字符数即为两个字符串的汉明距离。下面提供具体实现的代码,详细的算法讲解可参考网址https://blog.csdn.net/xcxy2015/article/details/77164126?utm_source=app&app_version=5.3.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

//创建一个方法 获取两个字符串差异操作数
    public int Levenshtein(String str1,String str2) {
    	int len1=str1.length();
    	int len2=str2.length();
    	//创建一个比字符串长度大一的二维空间
    	int[][] dif=new int[len1+1][len2+1];
    	for(int a=0;a<=len1;a++) {
    		dif[a][0]=a;
    	}
    	for(int a=0;a<=len2;a++) {
    		dif[0][a]=a;
    	}
    	//设标志 若上面值和左边值不相等 则左上角值加1 相等则加0
    	int temp;
    	for(int i=1;i<=len1;i++) {
    		for(int j=1;j<=len2;j++) {
    			if(str1.charAt(i-1)==str2.charAt(j-1)) {//上面值与左边值相等
    				temp=0;
    			}else {
    				temp=1;
    			}
    			//上面值和左边值都加1后 取上面值、左边值、左上角值中的最小值
    			dif[i][j]=min(dif[i-1][j]+1,dif[i][j-1]+1,dif[i-1][j-1]+temp);//调用最小值方法
    		}
    	}
    	//最右下角的数值即为操作数
    	return dif[len1][len2];
    } 
    //创建最小值方法 从一组数据中获取最小的数据值
    private static int min(int...is) {
    	int min=Integer.MAX_VALUE;
    	for(int i:is) {
    		if(min>i) {
    			min=i;
    		}
    	}
    	return min;
    }

第二种方法:先将汉明距离初始化为0,再遍历两个字符串,若两个字符串对应位置的字符不相等,则汉明距离加1,直至最小长度字符串遍历完得到最终的汉明距离。

int hashcodedistance=0;//初始化汉明距离
for(int i=0;i<Math.min(str1.length(),str2.length());i++){
    if(str1.charAt(i)!=str2.charAt(i)) {
        hashcodedistance++;
    }
}

有时图片对应的二进码字符串很长,比较次数很多,所以可将二进制码字符串先转为对应的十进制整数,再将十进制整数转为相应的十六进制码字符串,从而比较两个十六进制码字符串中不相等的字符数,减少比较次数。

步骤五:找出与给定图片指纹的最小汉明距离所对应的图片

创建一个保存键值对的泛型数组1,键为一组N张图片的图片路径,值为该N张图片所对应的编码数。再创建一个保存键值对的泛型数组2,键为该N张图片的图片路径,值为该N张图片与给定图片的汉明距离。获取泛型数组2中所有键值对中的值,将其转为数组进行排序,从而获取最小值。然后根据最小值访问其所对应的键,从而获取与给定图片最相近图片的图片路径,最后将其在窗体上绘出。

//定义一个泛型数组1 保存图片路径以及该图片所代表的二进制码字符串的键值对
static HashMap<String, String> imghashcodeMap1= new HashMap<> ();
int[][] imgarr=getpixelarray(path);//获取图片二维数组
String addimgstr=getimagestr(imgarr);//获取该图片的二进制码字符串
imghashcodeMap1.put(path, addimgstr);//将该图片的路径和二进制码字符串保存至数组1中
//定义一个泛型数组2 保存每张添加的图片路径与待匹配图片的字符操作数的键值对
HashMap<String, Integer> imghashcodeMap2= new HashMap<> ();
//遍历泛型数组1 计算每张添加图片与待配对图片的字符操作数
int difnum;//声明字符串差异操作数
for(String key : imghashcodeMap1.keySet ()){
    String addstr = imghashcodeMap1.get (key);
    //调用字符串差异操作数方法
    difnum=Levenshtein(addstr,imgstr);//addstr为添加图片路径 imgstr为给定图片路径
    imghashcodeMap2.put(key,difnum);//将添加图片路径与其所对应的字符串差异操作数保存至数组2中
}
//获取所有值 转为数组进行排序 从而获取最小的(汉明距离)
Collection<Integer> c=imghashcodeMap2.values();
Object[] obj=c.toArray();
Arrays.sort(obj);//按升序排序
//通过最小值获取其对应图片路径
String simimgpath="";//初始化图片路径
for(Entry<String, Integer> entry:imghashcodeMap2.entrySet()){
    if(entry.getValue().equals(obj[0])) {
       simimgpath=entry.getKey();//最相近图片路径
    }
}
	

项目实操:

从一组11张图片(douw0.jpg-douw10.jpg)中找到与douw13.jpg最相近的图片,并将douw13.jpg与最相近图片在窗体上绘出

 完整代码:

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map.Entry;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.filechooser.FileNameExtensionFilter;
public class Imagecmp2 extends JFrame {
	private static final long serialVersionUID = 1L;
	//定义一个泛型数组1 保存图片路径以及该图片所代表的二进制码字符串的键值对
	HashMap<String, String> imghashcodeMap1= new HashMap<> ();
	int[][] simimgarr= {{}};//初始化最相近图片二维数组
	int[][] matchimgarr= {{}};//初始化待匹配图片二维数组
	Graphics g=null;
	//创建窗体
	public void showUI() {
		setTitle("相似图片查询");
		setSize(800,800);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setLayout(new FlowLayout());
		//添加窗体按钮 实现自动加图片操作
		JButton btn1=new JButton("添加图库");
		JButton btn2=new JButton("查询图片");
		add(btn1);
		add(btn2);
		//打开添加图片监听器
		btn1.addActionListener(e->{
			for(int i=0;i<=10;i++) {
				String path="D:\\图片\\Pictures\\douw"+i+".jpg";
				System.out.println("添加的图片为:"+path);
				int[][] imgarr=getpixelarray(path);//获取图片二维数组
				String addimgstr=getimagestr(imgarr);//获取该图片的二进制码字符串
				imghashcodeMap1.put(path, addimgstr);//将该图片的路径和二进制码字符串保存至数组中
				System.out.println("添加的图片二进制码字符串为:"+addimgstr);
			}
			
		});
		//打开待匹配图片查询按钮
		btn2.addActionListener(e->{
			//文件选择器
			JFileChooser jfc=new JFileChooser();
			FileNameExtensionFilter filefilter=new FileNameExtensionFilter("图片文件","jpg", "png", "gif");
			jfc.setFileFilter(filefilter);
			int returnvale = jfc.showOpenDialog (null);
			if(returnvale == JFileChooser.APPROVE_OPTION){
				String path = jfc.getSelectedFile ().getPath ();
				System.out.println("待匹配图片为:"+path);
				matchimgarr=getpixelarray(path);//获取待匹配图片二维数组
				String imgstr=getimagestr(matchimgarr);//获取待匹配图片的二进制码字符串
				//创建泛型数组2 保存每张添加的图片路径与待匹配图片的字符操作数的键值对
				HashMap<String, Integer> imghashcodeMap2= new HashMap<> ();
				//遍历泛型数组1 计算每张添加图片与待配对图片的字符操作数
				int difnum;//声明字符串差异操作数
                for(String key : imghashcodeMap1.keySet ()){
                    String addstr = imghashcodeMap1.get (key);
                    //调用字符串差异操作数方法
                    difnum=Levenshtein(addstr,imgstr);
                    System.out.println("待匹配图片与"+key+"的字符串操作差异数为"+difnum);
                    imghashcodeMap2.put(key,difnum);
                }
              //获取所有值 转为数组进行排序 从而获取最小的(汉明距离)
               Collection<Integer> c=imghashcodeMap2.values();
               Object[] obj=c.toArray();
               Arrays.sort(obj);//按升序排序
               //通过最小值获取其对应图片路径
               String simimgpath="";//初始化图片路径
               for(Entry<String, Integer> entry:imghashcodeMap2.entrySet()){
            	   if(entry.getValue().equals(obj[0])) {
            		   simimgpath=entry.getKey();//最相近图片路径
            	   }
               }	
               System.out.println("与待匹配图片最相近的图片为"+simimgpath);
               simimgarr=getpixelarray(simimgpath);//最相近图片二维数组
			}
			//绘制待匹配图片
	        for(int k=0;k<matchimgarr.length;k++) {
	     	   for(int l=0;l<matchimgarr[0].length;l++) {
	     		   int rgb=matchimgarr[k][l];
	     		   Color color=new Color(rgb);
	     		   g.setColor(color);
	     		   g.fillRect(100+k, 100+l, 1, 1);
	     	   }
	        }
	        //绘制最相近图片
	        for(int k=0;k<simimgarr.length;k++) {
	     	   for(int l=0;l<simimgarr[0].length;l++) {
	     		   int rgb=simimgarr[k][l];
	     		   Color color=new Color(rgb);
	     		   g.setColor(color);
	     		   g.fillRect(400+k, 100+l, 1, 1);
	     	   }
	        }	
		});
    	setVisible(true);
    	//窗体获取画笔
    	g=getGraphics();
    	paint(g);
	}
	//将图片转为二维数组 保存其像素点
	public int[][] getpixelarray(String path){
		BufferedImage buffimg=null;
		try {
			buffimg=ImageIO.read(new File(path));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		int w=buffimg.getWidth();
		int h=buffimg.getHeight();
		int[][] imgarr=new int[w][h];
		for(int i=0;i<w;i++) {
			for(int j=0;j<h;j++) {
				imgarr[i][j]=buffimg.getRGB(i, j);
			}
		}
		return imgarr;
	}
	//获取8*8均值化灰度矩阵  遍历灰度矩阵获取二进制码字符串并将其返回
	public String getimagestr(int[][] imgarr) {
		String qrcodestr="";
		//将图片缩小 获取8*8的灰度矩阵
		int sw=imgarr.length/8;
		int sh=imgarr[0].length/8;
		int[][] grayarr=new int[8][8];//定义一个8*8的灰度矩阵
		//计算均值化灰度值 保存至灰度矩阵中
		for(int i=0;i<=imgarr.length-sw;i+=sw) {
			for(int j=0;j<=imgarr[i].length-sh;j+=sh) {
				int grayend=0;//在原先图片矩阵中每取一个sw*sh的矩阵都将灰度值初始化为0
				//遍历计算每次所取的sw*sh矩阵中的每个像素点的灰度值 进行求和并最终取平均值
				for(int k=0;k<sw;k++) {
					for(int l=0;l<sh;l++) {
						int rgb=imgarr[i+k][j+l];
						Color color=new Color(rgb);
						//计算灰度值
						int red=color.getRed();
						int green=color.getGreen();
						int blue=color.getBlue();
						int gray=(red+green+blue)/3;
						grayend+=gray;
					}
				}
					grayarr[i/sw][j/sh]=grayend/(sw*sh);//将均值化的灰度值保存至灰度矩阵中
			}
		}
		int avergray=0;
		//遍历灰度矩阵 计算灰度值的平均值
		for(int i=0;i<grayarr.length;i++) {
			for(int j=0;j<grayarr[i].length;j++) {
				avergray+=grayarr[i][j];
			}
		}
		avergray=avergray/(grayarr.length*grayarr[0].length);
		//再次遍历灰度矩阵 大于平均值则设置为0
		for(int k=0;k<grayarr.length;k++) {
			for(int l=0;l<grayarr[k].length;l++) {
				int gray=grayarr[k][l];
				if(gray>avergray) {
					qrcodestr+="0";
				}else {
					qrcodestr+="1";
				}
			}
		}
		return qrcodestr;
	}
	//创建一个方法 获取两个字符串差异操作数
    public int Levenshtein(String str1,String str2) {
    	int len1=str1.length();
    	int len2=str2.length();
    	//创建一个比字符串长度大一的二维空间
    	int[][] dif=new int[len1+1][len2+1];
    	for(int a=0;a<=len1;a++) {
    		dif[a][0]=a;
    	}
    	for(int a=0;a<=len2;a++) {
    		dif[0][a]=a;
    	}
    	//设标志 若上面值和左边值不相等 则左上角值加1 相等则加0
    	int temp;
    	for(int i=1;i<=len1;i++) {
    		for(int j=1;j<=len2;j++) {
    			if(str1.charAt(i-1)==str2.charAt(j-1)) {//上面值与左边值相等
    				temp=0;
    			}else {
    				temp=1;
    			}
    			//上面值和左边值都加1后 取上面值、左边值、左上角值中的最小值
    			dif[i][j]=min(dif[i-1][j]+1,dif[i][j-1]+1,dif[i-1][j-1]+temp);//调用最小值方法
    		}
    	}
    	//最右下角的数值即为操作数
    	return dif[len1][len2];
    } 
    //创建最小值方法 从一组数据中获取最小的数据值
    private static int min(int...is) {
    	int min=Integer.MAX_VALUE;
    	for(int i:is) {
    		if(min>i) {
    			min=i;
    		}
    	}
    	return min;
    }
	public static void main(String[]args) {
		Imagecmp2 imgcmp=new Imagecmp2();
		imgcmp.showUI();
	}
}

效果呈现:

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值