如何将 RectangleF 反转为 Picasa 面部哈希

发布于 2024-08-20 19:27:41 字数 2341 浏览 4 评论 0原文

以下是 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

寄意 2024-08-27 19:27:41

您当前的代码正在交换每个坐标的字节。这是因为 BitConverter 以小端顺序提供字节(即数组中的第一个字节是最低有效字节)。按如下方式交换分配可以使解码和重新编码返回原始哈希值。

        barray[0] = lb[1];
        barray[1] = lb[0];
        barray[2] = tb[1];
        barray[3] = tb[0];
        barray[4] = rb[1];
        barray[5] = rb[0];
        barray[6] = bb[1];
        barray[7] = bb[0];

也就是说,我认为使用简单的乘法和加法进行转换会更清楚。如果将哈希字符串作为单个 ulong 读入并进行减法/除法,则可以对哈希字符串进行类似的操作。例如对于编码:

    public static ushort ToUShort(double coordinate)
    {
        double ratio = Math.Max(0, Math.Min(Math.Round(coordinate * 65535), 65535));
        return (ushort)ratio;
    }

    public static string HashFromRectangle(Rect rect)
    {
        ulong left = ToUShort(rect.Left);
        ulong top = ToUShort(rect.Top);
        ulong right = ToUShort(rect.Right);
        ulong bottom = ToUShort(rect.Bottom);

        ulong hash = (((left * 65536) + top) * 65536 + right) * 65536 + bottom;
        return hash.ToString("x");
    }

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.

        barray[0] = lb[1];
        barray[1] = lb[0];
        barray[2] = tb[1];
        barray[3] = tb[0];
        barray[4] = rb[1];
        barray[5] = rb[0];
        barray[6] = bb[1];
        barray[7] = bb[0];

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:

    public static ushort ToUShort(double coordinate)
    {
        double ratio = Math.Max(0, Math.Min(Math.Round(coordinate * 65535), 65535));
        return (ushort)ratio;
    }

    public static string HashFromRectangle(Rect rect)
    {
        ulong left = ToUShort(rect.Left);
        ulong top = ToUShort(rect.Top);
        ulong right = ToUShort(rect.Right);
        ulong bottom = ToUShort(rect.Bottom);

        ulong hash = (((left * 65536) + top) * 65536 + right) * 65536 + bottom;
        return hash.ToString("x");
    }
衣神在巴黎 2024-08-27 19:27:41

看起来您需要从 HashFromRectangle(rect) 中取出浮点类型,如下所示:

    UInt16 left = (UInt16)( rect.Left * 65535.0F);
    UInt16 top =(UInt16) (rect.Top * 65535.0F);
    UInt16 right = (UInt16) (rect.Right * 65535.0F);
    UInt16 bottom = (UInt16) (rect.Bottom * 65535.0F);

另外,使用它来填充数组可能更具可读性:

    Array.Copy(lb, 0, barray, 0, 2);
    Array.Copy(tb, 0, barray, 2, 2);
    Array.Copy(rb, 0, barray, 4, 2);
    Array.Copy(bb, 0, barray, 6, 2);

让我知道这是否有效!

亚伦

It looks like you need to take out the float types from HashFromRectangle(rect) like so:

    UInt16 left = (UInt16)( rect.Left * 65535.0F);
    UInt16 top =(UInt16) (rect.Top * 65535.0F);
    UInt16 right = (UInt16) (rect.Right * 65535.0F);
    UInt16 bottom = (UInt16) (rect.Bottom * 65535.0F);

Also, it might be more readable to use this for filling in the array:

    Array.Copy(lb, 0, barray, 0, 2);
    Array.Copy(tb, 0, barray, 2, 2);
    Array.Copy(rb, 0, barray, 4, 2);
    Array.Copy(bb, 0, barray, 6, 2);

Let me know if this works!

Aaron

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文