using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Linq; //list集合的扩展
namespace W
{
// private static Bitmap ZoomImage(Bitmap bitmap, int destHeight, int destWidth)
// {
// try
// {
// System.Drawing.Image sourImage = bitmap;
// int width = 0, height = 0;
// //按比例缩放
// int sourWidth = sourImage.Width;
// int sourHeight = sourImage.Height;
// if (sourHeight > destHeight || sourWidth > destWidth)
// {
// if ((sourWidth * destHeight) > (sourHeight * destWidth))
// {
// width = destWidth;
// height = (destWidth * sourHeight) / sourWidth;
// }
// else
// {
// height = destHeight;
// width = (sourWidth * destHeight) / sourHeight;
// }
// }
// else
// {
// width = sourWidth;
// height = sourHeight;
// }
// Bitmap destBitmap = new Bitmap(destWidth, destHeight);
// Graphics g = Graphics.FromImage(destBitmap);
// g.Clear(Color.Transparent);
// //设置画布的描绘质量
// g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
// g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
// g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// g.DrawImage(sourImage, new Rectangle((destWidth - width) / 2, (destHeight - height) / 2, width, height), 0, 0, sourImage.Width, sourImage.Height, GraphicsUnit.Pixel);
// g.Dispose();
// //设置压缩质量
// System.Drawing.Imaging.EncoderParameters encoderParams = new System.Drawing.Imaging.EncoderParameters();
// long[] quality = new long[1];
// quality[0] = 100;
// System.Drawing.Imaging.EncoderParameter encoderParam = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
// encoderParams.Param[0] = encoderParam;
// sourImage.Dispose();
// return destBitmap;
// }
// catch
// {
// return bitmap;
// }
// }
////转换为灰度图:
// public static Bitmap ToGray(Bitmap bmp, out int avgGray)
// {
// int sum = 0;
// for (int i = 0; i < bmp.Width; i++)
// {
// for (int j = 0; j < bmp.Height; j++)
// {
// //获取该点的像素的RGB的颜色
// Color color = bmp.GetPixel(i, j);
// //利用公式计算灰度值
// int gray = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);
// Color newColor = Color.FromArgb(gray, gray, gray);
// sum += gray;
// bmp.SetPixel(i, j, newColor);
// }
// }
// avgGray = sum / (bmp.Width * bmp.Height);
// return bmp;
// }
////灰度指纹:(64位其实可以换成更高位数,比如用16x16,和8x8相比就要用4个64位进行表示)
// Bitmap bmp1 = ZoomImage((Bitmap)img1, 8, 8);
// Bitmap bmp2 = ZoomImage((Bitmap)img2, 8, 8);
// int avgGray1, avgGray2;
// bmp1 = ToGray(bmp1, out avgGray1);
// bmp2 = ToGray(bmp2, out avgGray2);
// UInt64 Hashdis1 = 0;
// UInt64 Hashdis2 = 0;
// UInt64 mask = 1;
// for (int x = 0; x < 8; x++)
// {
// for (int y = 0; y < 8; y++)
// {
// int m = y * 8 + x;
// Color c1 = bmp1.GetPixel(x, y);
// if (c1.R >= avgGray1)
// {
// Hashdis1 |= mask << m;
// }
// Color c2 = bmp1.GetPixel(x, y);
// if (c2.R >= avgGray2)
// {
// Hashdis2 |= mask << m;
// }
// }
// }
////得到汉明距离:
// UInt64 hash= Hashdis1 ^ Hashdis2;
// int Hammingdis = 0;
// for (int i = 0; i < 64; i++)
// {
// if ((hash & (mask << i)) != 0)
// Hammingdis++;
// }
// return Hammingdis;
////两个指纹进行异或运算,求不为0的位数(相异为1)
#region ahash算法
/*
均值哈希的基本思路
1、缩小尺寸:
去除图片的高频和细节的最快方法是缩小图片,将图片缩小到8x8的尺寸,总共64个像素。不要保持纵横比,只需将其变成8乘8的正方形。这样就可以比较任意大小的图片,摒弃不同尺寸、比例带来的图片差异。
2、简化色彩:
将8乘8的小图片转换成灰度图像。
3、计算平均值:
计算所有64个像素的灰度平均值。
4、比较像素的灰度:
将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
5、计算hash值:
将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
如果图片放大或缩小,或改变纵横比,结果值也不会改变。增加或减少亮度或对比度,或改变颜色,对hash值都不会太大的影响。最大的优点:计算速度快!
那么完成了以上步骤,一张图片就相当于有了自己的"指纹"了,然后就是计算不同位的个数,也就是汉明距离(例如1010001与1011101的汉明举例就是2,也就是不同的个数)。
如果汉明距离小于5,则表示有些不同,但比较相近,如果汉明距离大于10则表明完全不同的图片。
以上就是均值哈希的基本实现思路,总体来说是比较简单的。
————————————————
版权声明:本文为CSDN博主「床长」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jiangjunshow/article/details/100657183
*/
static public class ImageHashHelper
{
static public List<int> mathXList = new List<int>();
static public List<int> mathYList = new List<int>();
/// <summary>
/// 获取缩略图
/// </summary>
/// <returns></returns>
private static Bitmap GetThumbImage(Image image, int w, int h)
{
Bitmap bitmap = new Bitmap(w, h);
Graphics g = Graphics.FromImage(bitmap);
g.DrawImage(image,
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
return bitmap;
}
/// <summary>
/// 将图片转换为灰度图像
/// </summary>
/// <returns></returns>
private static Bitmap ToGray(Bitmap bmp)
{
try
{
Log.write("ImageHashHelper", "GetAvgHash", "bmp.Width;" + bmp.Width + ";bmp.Height:" + bmp.Height);
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
//获取该点的像素的RGB的颜色
Color color = bmp.GetPixel(x, y);
//利用公式计算灰度值
int gray = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);//计算方式1
//int gray1 = (int)((color.R + color.G + color.B) / 3.0M);//计算方式2
Color newColor = Color.FromArgb(gray, gray, gray);
bmp.SetPixel(x, y, newColor);
}
}
}
catch (Exception ex)
{
Log.write("ImageHashHelper", "ToGray", "" + ex.ToString());
}
return bmp;
}
/// <summary>
/// 获取图片的均值哈希
/// </summary>
/// <returns></returns>
/// GetAvgHash函数中获取64个像素的灰度值时直接通过了R来获取,因为RGB都是一样的,所以哪一个都可以。
public static int bitmapl = 32;
public static int bitmapw = 32;
static int bitmaps = bitmapl * bitmapw;
public static int[] GetAvgHash(Bitmap bitmap)
{
int[] code = new int[bitmaps];
try
{
//Log.write("ImageHashHelper", "GetAvgHash", "bitmaps;" + bitmaps + ";bitmapl:" + bitmapl + ";bitmapw:" + bitmapw);
Bitmap newBitmap = ToGray(GetThumbImage(bitmap, bitmapl, bitmapw));
//计算所有64个像素的灰度平均值。
List<int> allGray = new List<int>();
//Log.write("ImageHashHelper", "GetAvgHash", "bitmap.Width;" + newBitmap.Width + ";bitmap.Height:" + newBitmap.Height);
for (int row = 0; row < newBitmap.Width; row++)
{
for (int col = 0; col < newBitmap.Height; col++)
{
allGray.Add(newBitmap.GetPixel(row, col).R);
}
}
double avg = allGray.Average(a => a);//拿到平均值
//比较像素的灰度
for (int i = 0; i < allGray.Count; i++)
{
code[i] = allGray[i] >= avg ? 1 : 0;//将比较结果进行组合
}
}
catch (Exception ex)
{
Log.write("ImageHashHelper", "GetAvgHash", "" + ex.ToString());
}
//返回结果
return code;
}
/// <summary>
/// 获取图片的均值哈希
/// </summary>
/// <returns></returns>
/// GetAvgHash函数中获取64个像素的灰度值时直接通过了R来获取,因为RGB都是一样的,所以哪一个都可以。
public static int[] GetAvgHash(Bitmap bitmap,List<int> lx,List<int> ly)
{
if (lx.Count < 4 || ly.Count < 4) throw new Exception("List<int> lx,ly 必须为四边形的四个点的有效坐标");
int[] code = new int[bitmaps];
try
{
Log.write("ImageHashHelper", "GetAvgHash", "bitmaps;" + bitmaps + ";bitmapl:" + bitmapl + ";bitmapw:" + bitmapw);
Bitmap newBitmap = ToGray(GetThumbImage(bitmap, bitmapl, bitmapw));
//计算所有64个像素的灰度平均值。
List<int> allGray = new List<int>();
Log.write("ImageHashHelper", "GetAvgHash", "bitmap.Width;" + newBitmap.Width + ";bitmap.Height:" + newBitmap.Height);
int codeMack = 0;
for (int row = 0; row < newBitmap.Width; row++)
{
for (int col = 0; col < newBitmap.Height; col++)
{
if (isInTriangle2(lx[0], ly[0], lx[1], ly[1], lx[2], ly[2], row, col) && isInTriangle2(lx[0], ly[0], lx[3], ly[3], lx[2], ly[2], row, col))
{
//标记无效
code[codeMack++] = -1;
allGray.Add(newBitmap.GetPixel(row, col).R);
continue;
}
allGray.Add(newBitmap.GetPixel(row, col).R);
}
}
double avg = allGray.Average(a => a);//拿到平均值
//比较像素的灰度
for (int i = 0; i < allGray.Count; i++)
{
if( code[codeMack] == -1)continue;
code[i] = allGray[i] >= avg ? 1 : 0;//将比较结果进行组合
}
}
catch (Exception ex)
{
Log.write("ImageHashHelper", "GetAvgHash", "" + ex.ToString());
}
//返回结果
return code;
}
/// <summary>
/// 对两个AvgHash进行比较
/// </summary>
/// <returns></returns>
public static int Compare(int[] code1, int[] code2)
{
int v = 0;
for (int i = 0; i < bitmaps; i++)
{
if (code1[i] == code2[i])
{
v++;
}
}
return v;
}
/// <summary>
/// 对两个AvgHash进行比较
/// </summary>
/// <returns></returns>
/// 移除了去取均值的功能
public static int getAvg(int[] code)
{
int sum = 0;
for (int i = 0; i < bitmaps; i++)
{
if (code[i] == -1) continue;
sum += code[i];
}
return sum;
}
public static int getAvg4User(Bitmap bitmap)
{
return getAvg(GetAvgHash(bitmap));
}
public static int getAvg4UserInArea(Bitmap bitmap)
{
return getAvg(GetAvgHash(bitmap, mathXList, mathXList));
}
#endregion
#region 判断点是否在四边形内 (判断是否为凸四边形)
static double Xproduct(double ax, double ay, double bx, double by, double cx, double cy)
{
return (bx - ax) * (cy - ay) - (cx - ax) * (by - ay);
}
public static bool isInTriangle2(double ax, double ay, double bx, double by, double cx, double cy, double dx, double dy)
{
return (Xproduct(ax, ay, bx, by, dx, dy) >= 0 && Xproduct(bx, by, cx, cy, dx, dy) >= 0 && Xproduct(cx, cy, ax, ay, dx, dy) >= 0) ||
(Xproduct(ax, ay, bx, by, dx, dy) <= 0 && Xproduct(bx, by, cx, cy, dx, dy) <= 0 && Xproduct(cx, cy, ax, ay, dx, dy) <= 0);
}
#endregion
public static void getMathPoint(List<int> pointXList, List<int> pointYList)
{
if (pointYList.Count >= 4 && pointXList.Count >= 4)
{
mathXList = new List<int>();
mathYList = new List<int>();
int maxY = pointYList[0];
int maxX = pointXList[0];
int minX = pointXList[0];
int minY = pointYList[0];
foreach (int i in pointXList)
{
if (i > maxX)
{
maxX = i;
}
if (i < minX)
{
minX = i;
}
}
foreach (int j in pointYList)
{
if (j > maxY)
{
maxY = j;
}
if (j < minY)
{
minY = j;
}
}
for (int i = 0; i < pointXList.Count; i++)
{
pointXList[i] -= minX;
}
for (int i = 0; i < pointYList.Count; i++)
{
pointYList[i] -= minY;
}
for (int i = 0; i < pointXList.Count; i++)
{
if (pointXList[i] == 0)
{
mathXList.Add(0);
}
else
{
mathXList.Add(ImageHashHelper.bitmapw * ImageHashHelper.bitmapw / pointXList[i]);
}
}
for (int i = 0; i < pointYList.Count; i++)
{
if (pointYList[i] == 0)
{
mathYList.Add(0);
}
else
{
mathYList.Add(ImageHashHelper.bitmapl * ImageHashHelper.bitmapl / pointYList[i]);
}
}
}
else
{
return;
}
}
}
}
网友评论