如何将 RectangleF 反转为 Picasa 面部哈希
以下是 Picasa 以哈希形式存储的内容的详细信息。它像这样存储它们:
faces=rect64(54391dc9b6a76c2b),4cd643f64b715489
[DSC_2289.jpg]
faces=rect64(1680000a5c26c82),76bc8d8d518750bc
网络上的信息是这样说的:
rect64() 中封装的数字是一个 64 位十六进制数字。
- 将其分解为四个 16 位数字。
- 将每个数字除以最大无符号 16 位数字 (65535),您将得到 0 到 1 之间的四个数字。
- 剩下的四个数字为您提供面部矩形的相对坐标:(左、上、右、下)。
- 如果您想最终得到绝对坐标,请将左侧和右侧乘以图像宽度,将顶部和底部乘以图像高度。
因此,我将其转换为 RectangleF 的代码工作得很好(仅保留相对坐标):
public static RectangleF GetRectangle(string hashstr)
{
UInt64 hash = UInt64.Parse(hashstr, System.Globalization.NumberStyles.HexNumber);
byte[] bytes = BitConverter.GetBytes(hash);
UInt16 l16 = BitConverter.ToUInt16(bytes, 6);
UInt16 t16 = BitConverter.ToUInt16(bytes, 4);
UInt16 r16 = BitConverter.ToUInt16(bytes, 2);
UInt16 b16 = BitConverter.ToUInt16(bytes, 0);
float left = l16 / 65535.0F;
float top = t16 / 65535.0F;
float right = r16 / 65535.0F;
float bottom = b16 / 65535.0F;
return new RectangleF(left, top, right - left, bottom - top);
}
现在我有一个 RectangleF,我想将其转换回上面提到的哈希值。我似乎无法弄清楚这一点。看起来 picasa 使用 2 个字节(包括精度),但 C# 中的浮点数是 8 个字节,甚至 BitConverter.ToSingle 也是 4 个字节。
任何帮助表示赞赏。
编辑:这是我现在所拥有的
public static string HashFromRectangle(RectangleCoordinates rect)
{
Console.WriteLine("{0} {1} {2} {3}", rect.Left, rect.Top, rect.Right, rect.Bottom);
UInt16 left = Convert.ToUInt16((float)rect.Left * 65535.0F);
UInt16 top = Convert.ToUInt16((float)rect.Top * 65535.0F);
UInt16 right = Convert.ToUInt16((float)rect.Right * 65535.0F);
UInt16 bottom = Convert.ToUInt16((float)rect.Bottom * 65535.0F);
byte[] lb = BitConverter.GetBytes(left);
byte[] tb = BitConverter.GetBytes(top);
byte[] rb = BitConverter.GetBytes(right);
byte[] bb = BitConverter.GetBytes(bottom);
byte[] barray = new byte[8];
barray[0] = lb[0];
barray[1] = lb[1];
barray[2] = tb[0];
barray[3] = tb[1];
barray[4] = rb[0];
barray[5] = rb[1];
barray[6] = bb[0];
barray[7] = bb[1];
return BitConverter.ToString(barray).Replace("-", "").ToLower();
}
Here are the details for what Picasa stores as a hash. It stores them like this:
faces=rect64(54391dc9b6a76c2b),4cd643f64b715489
[DSC_2289.jpg]
faces=rect64(1680000a5c26c82),76bc8d8d518750bc
Info on the web says this:
The number encased in rect64() is a 64-bit hexadecimal number.
- Break that up into four 16-bit numbers.
- Divide each by the maximum unsigned 16-bit number (65535) and you'll have four numbers between 0 and 1.
- The four numbers remaining give you relative coordinates for the face rectangle: (left, top, right, bottom).
- If you want to end up with absolute coordinates, multiple the left and right by the image width and the top and bottom by the image height.
So my code to turn that into a RectangleF works just fine (only keeping relative coordinates):
public static RectangleF GetRectangle(string hashstr)
{
UInt64 hash = UInt64.Parse(hashstr, System.Globalization.NumberStyles.HexNumber);
byte[] bytes = BitConverter.GetBytes(hash);
UInt16 l16 = BitConverter.ToUInt16(bytes, 6);
UInt16 t16 = BitConverter.ToUInt16(bytes, 4);
UInt16 r16 = BitConverter.ToUInt16(bytes, 2);
UInt16 b16 = BitConverter.ToUInt16(bytes, 0);
float left = l16 / 65535.0F;
float top = t16 / 65535.0F;
float right = r16 / 65535.0F;
float bottom = b16 / 65535.0F;
return new RectangleF(left, top, right - left, bottom - top);
}
Now I have a RectangleF and I want to turn it back into the hash mentioned above. I can't seem to figure this out. It looks like picasa uses 2 bytes including precision, however a float in C# is 8 bytes, and even BitConverter.ToSingle is 4 bytes.
Any help appreciated.
EDIT: Here is what I have right now
public static string HashFromRectangle(RectangleCoordinates rect)
{
Console.WriteLine("{0} {1} {2} {3}", rect.Left, rect.Top, rect.Right, rect.Bottom);
UInt16 left = Convert.ToUInt16((float)rect.Left * 65535.0F);
UInt16 top = Convert.ToUInt16((float)rect.Top * 65535.0F);
UInt16 right = Convert.ToUInt16((float)rect.Right * 65535.0F);
UInt16 bottom = Convert.ToUInt16((float)rect.Bottom * 65535.0F);
byte[] lb = BitConverter.GetBytes(left);
byte[] tb = BitConverter.GetBytes(top);
byte[] rb = BitConverter.GetBytes(right);
byte[] bb = BitConverter.GetBytes(bottom);
byte[] barray = new byte[8];
barray[0] = lb[0];
barray[1] = lb[1];
barray[2] = tb[0];
barray[3] = tb[1];
barray[4] = rb[0];
barray[5] = rb[1];
barray[6] = bb[0];
barray[7] = bb[1];
return BitConverter.ToString(barray).Replace("-", "").ToLower();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您当前的代码正在交换每个坐标的字节。这是因为 BitConverter 以小端顺序提供字节(即数组中的第一个字节是最低有效字节)。按如下方式交换分配可以使解码和重新编码返回原始哈希值。
也就是说,我认为使用简单的乘法和加法进行转换会更清楚。如果将哈希字符串作为单个
ulong
读入并进行减法/除法,则可以对哈希字符串进行类似的操作。例如对于编码:Your current code is swapping the bytes of each coordinate. This is because BitConverter gives you the bytes in little endian order (i.e. the first byte in the array is the least significant byte). Swapping around your assignments as follows makes decoding and re-encoding give back the original hash.
That said, I think it's clearer to do the conversion using simple multiplies and adds. You can do a similar thing with the decoding of the hash string if you read it in as a single
ulong
and subtract/divide. e.g. for the encoding:看起来您需要从 HashFromRectangle(rect) 中取出浮点类型,如下所示:
另外,使用它来填充数组可能更具可读性:
让我知道这是否有效!
亚伦
It looks like you need to take out the float types from HashFromRectangle(rect) like so:
Also, it might be more readable to use this for filling in the array:
Let me know if this works!
Aaron