Java相似图片搜索算法之"感知哈希算法"实例

Java 相似图片搜索算法之感知哈希算法实例:


import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;


public class SimilarImageSearch {
  
  public static final int SAMEVALUE = 5; //相同图片阀值
  public static final int SIMILARVALUE = 10; //相似图片阀值
 
  public static void main(String[] args) {
    List hashCodes = new ArrayList();
    
    String searchImgUrl = “C:\\Users\\admin\\Desktop\\image\\example.jpg”;
    List urlList = collectionImgUrl();
    String hashCode = null;
    long startMillis = System.currentTimeMillis();
    
    for (String url : urlList) {
      hashCode = produceFingerPrint(url);
      hashCodes.add(hashCode);
    }
    
    System.out.println(“Resources: “);
    System.out.println(hashCodes);
    System.out.println();


    String sourceHashCode = produceFingerPrint(searchImgUrl);
    System.out.println(“Source: “);
    System.out.println(sourceHashCode);
    System.out.println();


    List resultList = new ArrayList();
    List similarResultList = new ArrayList();
    List differences = new ArrayList();
    
    for (int i = 0; i < hashCodes.size(); i++) {
      int difference = hammingDistance(sourceHashCode, hashCodes.get(i));
      if(difference <= SAMEVALUE){
       resultList.add(urlList.get(i).substring(urlList.get(i).lastIndexOf(“\”)+1,urlList.get(i).length()));
      }else if(difference <= SIMILARVALUE){
       similarResultList.add(urlList.get(i).substring(urlList.get(i).lastIndexOf(“\”)+1,urlList.get(i).length()));
      }
      differences.add(difference+”->”+urlList.get(i).substring(urlList.get(i).lastIndexOf(“\”)+1,urlList.get(i).length()));
    }
    
    System.out.println(“curMillis:”+(System.currentTimeMillis()-startMillis));
    System.out.println(“搜索图片:”+searchImgUrl.substring(searchImgUrl.lastIndexOf(“\”)+1,searchImgUrl.length()));
    System.out.println(“相同图片:”+resultList);
    System.out.println(“相似图片:”+similarResultList);
    System.out.println(“图片对比:”+differences);
  }
  
 
  public static List collectionImgUrl(){
    String imgPath = “C:\\Users\\admin\\Desktop\\image”;
    
    List list = new ArrayList();
    File file = new File(imgPath);
    
    if(file.isDirectory()){
      String[] fileNames = file.list();
      for(String name : fileNames){
        list.add(imgPath.concat(“\”)+name);
      }
    }
    
    return list;
  }


 
  public static int hammingDistance(String sourceHashCode, String hashCode) {
    int difference = 0;
    int len = sourceHashCode.length();


    for (int i = 0; i < len; i++) {
      if (sourceHashCode.charAt(i) != hashCode.charAt(i)) {
        difference++;
      }
    }


    return difference;
  }


 
  public static String produceFingerPrint(String filename) {
    BufferedImage source = ImageHelper.readPNGImage(filename);// 读取文件


    int width = 8;
    int height = 8;


    // 第一步,缩小尺寸。
    // 将图片缩小到8×8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
    BufferedImage thumb = ImageHelper.thumb(source, width, height, false);


    // 第二步,简化色彩。
    // 将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
    int[] pixels = new int[width * height];
    for (int i = 0; i < width; i++) {
      for (int j = 0; j < height; j++) {
        pixels[i * height + j] = ImageHelper.rgbToGray(thumb.getRGB(i, j));
      }
    }


    // 第三步,计算平均值。
    // 计算所有64个像素的灰度平均值。
    int avgPixel = ImageHelper.average(pixels);


    // 第四步,比较像素的灰度。
    // 将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
    int[] comps = new int[width * height];
    for (int i = 0; i < comps.length; i++) {
      if (pixels[i] >= avgPixel) {
        comps[i] = 1;
      } else {
        comps[i] = 0;
      }
    }


    // 第五步,计算哈希值。
    // 将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
    StringBuffer hashCode = new StringBuffer();
    for (int i = 0; i < comps.length; i += 4) {
      int result = comps[i] * (int) Math.pow(2, 3) + comps[i + 1]
          * (int) Math.pow(2, 2) + comps[i + 2] * (int) Math.pow(2, 1)
          + comps[i + 2];
      hashCode.append(binaryToHex(result));
    }


    // 得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。
    return hashCode.toString();
  }


 
  private static char binaryToHex(int binary) {
    char ch = ‘ ‘;
    switch (binary) {
    case 0:
      ch = ‘0’;
      break;
    case 1:
      ch = ‘1’;
      break;
    case 2:
      ch = ‘2’;
      break;
    case 3:
      ch = ‘3’;
      break;
    case 4:
      ch = ‘4’;
      break;
    case 5:
      ch = ‘5’;
      break;
    case 6:
      ch = ‘6’;
      break;
    case 7:
      ch = ‘7’;
      break;
    case 8:
      ch = ‘8’;
      break;
    case 9:
      ch = ‘9’;
      break;
    case 10:
      ch = ‘a’;
      break;
    case 11:
      ch = ‘b’;
      break;
    case 12:
      ch = ‘c’;
      break;
    case 13:
      ch = ‘d’;
      break;
    case 14:
      ch = ‘e’;
      break;
    case 15:
      ch = ‘f’;
      break;
    default:
      ch = ‘ ‘;
    }
    return ch;
  }
}


import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;


import javax.imageio.ImageIO;


import com.sun.image.codec.jpeg.ImageFormatException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.sun.image.codec.jpeg.JPEGImageEncoder;


public class ImageHelper {


  // 项目根目录路径
  public static final String path = System.getProperty(“user.dir”);


 
  public static BufferedImage thumb(BufferedImage source, int width,
      int height, boolean b) {
    // targetW,targetH分别表示目标长和宽
    int type = source.getType();
    BufferedImage target = null;
    double sx = (double) width / source.getWidth();
    double sy = (double) height / source.getHeight();


    if (b) {
      if (sx > sy) {
        sx = sy;
        width = (int) (sx * source.getWidth());
      } else {
        sy = sx;
        height = (int) (sy * source.getHeight());
      }
    }


    if (type == BufferedImage.TYPE_CUSTOM) { // handmade
      ColorModel cm = source.getColorModel();
      WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
      boolean alphaPremultiplied = cm.isAlphaPremultiplied();
      target = new BufferedImage(cm, raster, alphaPremultiplied, null);
    } else
      target = new BufferedImage(width, height, type);
    Graphics2D g = target.createGraphics();
    // smoother than exlax:
    g.setRenderingHint(RenderingHints.KEY_RENDERING,
        RenderingHints.VALUE_RENDER_QUALITY);
    g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
    g.dispose();
    return target;
  }


 
  public static void waterMark(String imgPath, String markPath, int x, int y,
      float alpha) {
    try {
      // 加载待处理图片文件
      Image img = ImageIO.read(new File(imgPath));


      BufferedImage image = new BufferedImage(img.getWidth(null),
          img.getHeight(null), BufferedImage.TYPE_INT_RGB);
      Graphics2D g = image.createGraphics();
      g.drawImage(img, 0, 0, null);


      // 加载水印图片文件
      Image src_biao = ImageIO.read(new File(markPath));
      g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
      g.drawImage(src_biao, x, y, null);
      g.dispose();


      // 保存处理后的文件
      FileOutputStream out = new FileOutputStream(imgPath);
      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
      encoder.encode(image);
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }


 


  public static void textMark(String imgPath, String text, Font font,
      Color color, int x, int y, float alpha) {
    try {
      Font Dfont = (font == null) ? new Font(“宋体”, 20, 13) : font;


      Image img = ImageIO.read(new File(imgPath));


      BufferedImage image = new BufferedImage(img.getWidth(null),
          img.getHeight(null), BufferedImage.TYPE_INT_RGB);
      Graphics2D g = image.createGraphics();


      g.drawImage(img, 0, 0, null);
      g.setColor(color);
      g.setFont(Dfont);
      g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
      g.drawString(text, x, y);
      g.dispose();
      FileOutputStream out = new FileOutputStream(imgPath);
      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
      encoder.encode(image);
      out.close();
    } catch (Exception e) {
      System.out.println(e);
    }
  }


 
  public static BufferedImage readJPEGImage(String filename) {
    try {
      InputStream imageIn = new FileInputStream(new File(filename));
      // 得到输入的编码器,将文件流进行jpg格式编码
      JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(imageIn);
      // 得到编码后的图片对象
      BufferedImage sourceImage = decoder.decodeAsBufferedImage();


      return sourceImage;
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (ImageFormatException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }


    return null;
  }


 
  public static BufferedImage readPNGImage(String filename) {
    try {
      File inputFile = new File(filename);
      BufferedImage sourceImage = ImageIO.read(inputFile);
      return sourceImage;
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (ImageFormatException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }


    return null;
  }


 
  public static int rgbToGray(int pixels) {
    // int _alpha = (pixels >> 24) & 0xFF;
    int _red = (pixels >> 16) & 0xFF;
    int _green = (pixels >> 8) & 0xFF;
    int _blue = (pixels) & 0xFF;
    return (int) (0.3 * _red + 0.59 * _green + 0.11 * _blue);
  }


 
  public static int average(int[] pixels) {
    float m = 0;
    for (int i = 0; i < pixels.length; ++i) {
      m += pixels[i];
    }
    m = m / pixels.length;
    return (int) m;
  }
}


转自http://blog.sina.com.cn/s/blog_5ddc071f0101o9th.html

    原文作者:哈希算法
    原文地址: https://blog.csdn.net/u011402197/article/details/48677719
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞