前段時間公司項目用到了語音識別,圖像識別,視頻識別等,其實不能說是識別,應該說是類似度對比吧,畢竟類似度對比還上升不了到識別哈,等之後有了更深的理解再來討論修改下!此次就當作一個總結吧!java
其實它的原理就是一個把須要的特徵總結在一個指紋碼裏面,進行降維成指紋碼,假如個指紋碼如出一轍,那兩張圖片就想似了.下面有寫怎麼編譯成惟一標識,再用漢明距離計算兩個指紋碼的類似度.算法
圖片是採用phash算法,一共分爲四步吧.數組
1.將圖片縮放到16*16大小,這是咱們選擇的合適的大小,假如寬高不同,直接將其壓到16*16,去掉細節,只保留宏觀;app
2.圖片一共是16*16的,共256個像素,咱們將圖片進行灰度化,灰度化就是隻有黑白灰三種,從白到黑,一共分了255層;框架
3.灰度化以後將圖片進行DCT轉換(離散餘弦變化),由於爲了識別有的圖片旋轉,這個DCT轉換是將圖片進行了一種壓縮算法;ide
4.咱們對這個算法進行了優化,由於以前是計算像素的均值,咱們爲了更準確,咱們取RGB,rgb一共分爲255個像素,咱們將255個像素分爲16段,若是像素大於0-16記爲0,17到32記爲1,直到255,這樣就獲得255位的二進制,這就是這張圖片的指紋碼.測試
獲得惟一標識的指紋碼以後怎麼去計算像素度呢?優化
經過漢明距離比較兩個二進制距離,若是距離小於<10的話,咱們就斷定兩張圖片類似.若是兩個指紋碼(二進制)如出一轍,咱們就斷定兩個是一張圖片,或者相似;this
視頻的話咱們是經過ffmpeg(ff am pig),它是一個專門處理視頻的框架,能夠從視頻中按針提取圖片.而後就按照圖片的類似度取對比了...spa
package net.gdface.image; import java.awt.Graphics; import java.awt.Image; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorConvertOp; import java.util.Arrays; /** * 均值哈希實現圖像指紋比較 * @author test * */ public final class FingerPrint { /** * 圖像指紋的尺寸,將圖像resize到指定的尺寸,來計算哈希數組 */ private static final int HASH_SIZE=16; /** * 保存圖像指紋的二值化矩陣 */ private final byte[] binaryzationMatrix; public FingerPrint(byte[] hashValue) { if(hashValue.length!=HASH_SIZE*HASH_SIZE) throw new IllegalArgumentException(String.format("length of hashValue must be %d",HASH_SIZE*HASH_SIZE )); this.binaryzationMatrix=hashValue; } public FingerPrint(String hashValue) { this(toBytes(hashValue)); } public FingerPrint (BufferedImage src){ this(hashValue(src)); } private static byte[] hashValue(BufferedImage src){ BufferedImage hashImage = resize(src,HASH_SIZE,HASH_SIZE); byte[] matrixGray = (byte[]) toGray(hashImage).getData().getDataElements(0, 0, HASH_SIZE, HASH_SIZE, null); return binaryzation(matrixGray); } /** * 從壓縮格式指紋建立{@link FingerPrint}對象 * @param compactValue * @return */ public static FingerPrint createFromCompact(byte[] compactValue){ return new FingerPrint(uncompact(compactValue)); } public static boolean validHashValue(byte[] hashValue){ if(hashValue.length!=HASH_SIZE) return false; for(byte b:hashValue){ if(0!=b&&1!=b)return false; } return true; } public static boolean validHashValue(String hashValue){ if(hashValue.length()!=HASH_SIZE) return false; for(int i=0;i<hashValue.length();++i){ if('0'!=hashValue.charAt(i)&&'1'!=hashValue.charAt(i))return false; } return true; } public byte[] compact(){ return compact(binaryzationMatrix); } /** * 指紋數據按位壓縮 * @param hashValue * @return */ private static byte[] compact(byte[] hashValue){ byte[] result=new byte[(hashValue.length+7)>>3]; byte b=0; for(int i=0;i<hashValue.length;++i){ if(0==(i&7)){ b=0; } if(1==hashValue[i]){ b|=1<<(i&7); }else if(hashValue[i]!=0) throw new IllegalArgumentException("invalid hashValue,every element must be 0 or 1"); if(7==(i&7)||i==hashValue.length-1){ result[i>>3]=b; } } return result; } /** * 壓縮格式的指紋解壓縮 * @param compactValue * @return */ private static byte[] uncompact(byte[] compactValue){ byte[] result=new byte[compactValue.length<<3]; for(int i=0;i<result.length;++i){ if((compactValue[i>>3]&(1<<(i&7)))==0) result[i]=0; else result[i]=1; } return result; } /** * 字符串類型的指紋數據轉爲字節數組 * @param hashValue * @return */ private static byte[] toBytes(String hashValue){ hashValue=hashValue.replaceAll("\\s", ""); byte[] result=new byte[hashValue.length()]; for(int i=0;i<result.length;++i){ char c = hashValue.charAt(i); if('0'==c) result[i]=0; else if('1'==c) result[i]=1; else throw new IllegalArgumentException("invalid hashValue String"); } return result; } /** * 縮放圖像到指定尺寸 * @param src * @param width * @param height * @return */ private static BufferedImage resize(Image src,int width,int height){ BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); Graphics g = result.getGraphics(); try{ g.drawImage(src.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null); }finally{ g.dispose(); } return result; } /** * 計算均值 * @param src * @return */ private static int mean(byte[] src){ long sum=0; // 將數組元素轉爲無符號整數 for(byte b:src)sum+=(long)b&0xff; return (int) (Math.round((float)sum/src.length)); } /** * 二值化處理 * @param src * @return */ private static byte[] binaryzation(byte[]src){ byte[] dst = src.clone(); int mean=mean(src); for(int i=0;i<dst.length;++i){ // 將數組元素轉爲無符號整數再比較 dst[i]=(byte) (((int)dst[i]&0xff)>=mean?1:0); } return dst; } /** * 轉灰度圖像 * @param src * @return */ private static BufferedImage toGray(BufferedImage src){ if(src.getType()==BufferedImage.TYPE_BYTE_GRAY){ return src; }else{ // 圖像轉灰 BufferedImage grayImage = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_BYTE_GRAY); new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null).filter(src, grayImage); return grayImage; } } @Override public String toString() { return toString(true); } /** * @param multiLine 是否分行 * @return */ public String toString(boolean multiLine) { StringBuffer buffer=new StringBuffer(); int count=0; for(byte b:this.binaryzationMatrix){ buffer.append(0==b?'0':'1'); if(multiLine&&++count%HASH_SIZE==0) buffer.append('\n'); } return buffer.toString(); } @Override public boolean equals(Object obj) { if(obj instanceof FingerPrint){ return Arrays.equals(this.binaryzationMatrix,((FingerPrint)obj).binaryzationMatrix); }else return super.equals(obj); } /** * 與指定的壓縮格式指紋比較類似度 * @param compactValue * @return * @see #compare(FingerPrint) */ public float compareCompact(byte[] compactValue){ return compare(createFromCompact(compactValue)); } /** * @param hashValue * @return * @see #compare(FingerPrint) */ public float compare(String hashValue){ return compare(new FingerPrint(hashValue)); } /** * 與指定的指紋比較類似度 * @param hashValue * @return * @see #compare(FingerPrint) */ public float compare(byte[] hashValue){ return compare(new FingerPrint(hashValue)); } /** * 與指定圖像比較類似度 * @param image2 * @return * @see #compare(FingerPrint) */ public float compare(BufferedImage image2){ return compare(new FingerPrint(image2)); } /** * 比較指紋類似度 * @param src * @return * @see #compare(byte[], byte[]) */ public float compare(FingerPrint src){ if(src.binaryzationMatrix.length!=this.binaryzationMatrix.length) throw new IllegalArgumentException("length of hashValue is mismatch"); return compare(binaryzationMatrix,src.binaryzationMatrix); } /** * 判斷兩個數組類似度,數組長度必須一致不然拋出異常 * @param f1 * @param f2 * @return 返回類似度(0.0~1.0) */ private static float compare(byte[] f1,byte[] f2){ if(f1.length!=f2.length) throw new IllegalArgumentException("mismatch FingerPrint length"); int sameCount=0; for(int i=0;i<f1.length;++i){ if(f1[i]==f2[i])++sameCount; } return (float)sameCount/f1.length; } public static float compareCompact(byte[] f1,byte[] f2){ return compare(uncompact(f1),uncompact(f2)); } public static float compare(BufferedImage image1,BufferedImage image2){ return new FingerPrint(image1).compare(new FingerPrint(image2)); } }
junit測試代碼
package test; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import org.junit.Test; import net.gdface.image.FingerPrint; import net.gdface.image.NotImage; import net.gdface.image.UnsupportedFormat; public class TestFingerPrint { @Test public void testCompare() throws IOException{ FingerPrint fp1 = new FingerPrint(ImageIO.read(new File("d:\\tmp\\he049-black.jpg"))); FingerPrint fp2 =new FingerPrint(ImageIO.read(new File("d:\\tmp\\he049-gray.jpg"))); System.out.println(fp1.toString(true)); System.out.printf("sim=%f",fp1.compare(fp2)); } }