到2016年的最后一个月了,总得写点什么或者记录点什么。恩,就记录一下,我当时做以图找图,这个功能的过程和结果吧。以图找图简而言之就是确认小图是否属于大图的一部分。
这个方法不是百分百能找到的,也可能找到错误的图片,如果2个大图和小图的相似度很高的话。
1.序
刚开始做这个功能的时候,我一直在往误区上找,认为这是Android的项目,然后一直在找android方面的东西,也确实找到了,但这方面根本没有相关的DEMO(其实也是因为个人有点懒,有现成的,总是想直接用。)
然后想到了其实可以从java方面图片,这样就发现很多都是通过像素点进行查找的,但原理上是没错的就是查找的过程就有点慢了。在电脑上运行当然很快,但放在android上就很慢了,毕竟你需要对比每个像素点。
最后是通过计算哈希值的方法,来进行图片的查找。虽然也需要一块一块的查(就是根据原图片的大小,然后在目标图片上查找),然后得出结论是否是属于目标图片的。
2.代码
/** 小图是否属于大图
* @param mubiao 目标图片(小图)地址
* @param yuantu 源图片(大图)地址
* @return
* @throws IOException
*/
private static boolean FindImg(String mubiao, String yuantu) throws IOException{
boolean isFind = false;
Bitmap yuantusource = BitmapFactory.decodeFile(yuantu, null);
Bitmap mubiaosource = BitmapFactory.decodeFile(mubiao, null);
// huidu("yuantu" , yuantu);
// huidu("mubiao" , mubiao);
String mubiaoHashCode;
String yuantuHashCode;
Bitmap jiequsource;
int width = yuantusource.getWidth();
int height = yuantusource.getHeight();
int Mwidth = mubiaosource.getWidth();
int Mheight = mubiaosource.getHeight();
mubiaoHashCode = BufproduceFingerPrint(mubiaosource);
//通过循环来查找图片(就是从左上到右下)
for(int i=0;i<width-Mwidth;i++){
for(int j= 0;j<height-Mheight;j++){
jiequsource = Bitmap.createBitmap(yuantusource, i, j, Mwidth, Mheight,
null, false);
yuantuHashCode = BufproduceFingerPrint(jiequsource);
int difference = hammingDistance(mubiaoHashCode, yuantuHashCode);
if(difference == 0 ){
String.valueOf(j));
// LogInfo.ceshi("找到2:x="+ i + "y=" + j);
isFind = true;
break;
} else{
// LogInfo.ceshi("没找到2:x="+ i + "y=" + j);
// ToastUtil.showToast(context, "正在查找中···");
}
}
}
if(isFind){
return true;
}else{
return false;
}
}
/**
* 处理图片
* @param source
* @return
*/
public static String BufproduceFingerPrint(Bitmap source) {
// BufferedImage source = ImageHelper.readPNGImage(filename);// 读取文件
int width = 8;
int height = 8;
int pixelColor;
// 第一步,缩小尺寸。
// 将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
Bitmap thumb = ImageHelper.zoomImage(source, width,height);
// 第二步,简化色彩。
// 将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
int[] pixels = new int[width * height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
// System.out.println("i=" + i +";y=" + j);
// pixelColor = thumb.getPixel(i, j);
// R = Color.red(pixelColor);
// G = Color.green(pixelColor);
// B = Color.blue(pixelColor);
pixels[i * height + j] = ImageHelper.rgbToGray(thumb.getPixel(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;
}
/**
* 2个是否相同,0为相同
* @param sourceHashCode
* @param hashCode
* @return
*/
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;
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
public class ImageHelper {
public static Bitmap zoomImage(Bitmap bgimage, double newWidth,
double newHeight) {
// 获取这个图片的宽和高
float width = bgimage.getWidth();
float height = bgimage.getHeight();
// 创建操作图片用的matrix对象
Matrix matrix = new Matrix();
// 计算宽高缩放率
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// 缩放图片动作
matrix.postScale(scaleWidth, scaleHeight);
Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width,
(int) height, matrix, true);
return bitmap;
}
private Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 100;
while ( baos.toByteArray().length / 1024>100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset();//重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
options -= 10;//每次都减少10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
return bitmap;
}
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;
}
}
3.结束
恩,这就是以图找图的所有代码=-=恩,是通过网上方法总结出来了的=-=就这样了。