Convert Signature Bitmap to Signature String (very strange one)

前端 未结 1 776
星月不相逢
星月不相逢 2021-01-28 20:27

Basically I need to convert a bitmap image into a string but it is not a common one.

The dilemma is that this string is composed of two parts:

1) Points 2) Lines

相关标签:
1条回答
  • 2021-01-28 21:15

    I figured the encoding out from the code you posted (had to port it to C++ though) so the encoding:

    1. string starts with list of points

      Each point is present as 4-digit hex number. First 2 hex digits are x coordinate and the second 2 digits are y coordinate of point. (probably marking each mouse down event so it holds the info on how many continuous draws the image was drawn)

    2. then separator * follows

    3. after that list of lines is present

      Each line contains 2 points so total of 8-digits hex number per line. The sequence is x0,y0,x1,y1 2-digit hex number per coordinate. If the string contains only 4-digit hex number (the end) it marks the end of signature string.

    So when you want to make own signature code then:

    1. clear list of points and list of lines

    2. on mouse down (left button click on or pen hit) event

      Add current mouse/pen position to the point list and as a start point to line list

    3. on mouse/pen move event

      First find out if the mouse button is still clicked or pen still hitting the pad. If not ignore this event. If yes then add current mouse position to line list twice (that is why they are duplicated the first ends current line and the second starts new line)

    4. on mouse/pen up event

      close the actual line so add current mouse position to line list once

    5. before exporting

      duplicate last point in lines list to mark end of string

    That is all. If you need to convert already drawed image (raster) then you may have problems with comparison because is very unlikely you vectorize the image the same way as the author signs it. Resulting in miss-match of the same images. Also the tripled last point is most likely because of wrong encoding of mouse/pen up event adding the same point inside on mouse move and on mouse up events at once

    The images encoded this way are limited to 256x256 pixels.

    Here example of decoded image for your first signature string:

    signature="221A*221A270A270A2503250320072007171617160D2A0D2A07380738073F073F0B3E0B3E15311531222122212C182C183016301631173117311A311A302230222E272E272D2C2D2C2C2F2C2F2C312C312C2F2C2F2E2B2E2B3126312633223322371D371D381C381C3B1B3B1B3C1C3C1C3D1F3D1F3D243D243C2D3C2D3A333A333A363A36393939393B383B383D363D36412E412E46264626492049204B1B4B1B4E184E184F174F175017501751185118511D511D51225122502450244F294F294F2C4F2C4E2F4E2F4F314F315030503052305230552C552C582958295D225D22601E601E611C611C621B621B621A621A601A601A5D1B5D1B5B1E5B1E5723572353285328512B512B502E502E502F502F513151315231523154305430582E582E592D592D5C2C5C2C5F2A5F2A6428642865286528692669266D246D247122712275207520791D791D7D1B7D1B81198119841884188618861887198719881A881A891B891B881C881C871E871E861F861F852085208421842182228222812481247F257F257E257E257D257D257C247C247C217C217D1F7D1F7E1C7E1C801A801A81198119821782178317831784188418851A851A851C851C86208620862286228625862587278727872B872B882E882E893289328A338A338E348E349033903393329332972F972F9A2D9A2D9F299F29A426A426AB20AB20AF1CAF1CB517B517B716B716B716";

    here square example

    signature="0808*080820082008202020200820082008080808";

    so when reordered:

    0808  // point(8h,8h);
    *     // separator
    08 08 20 08 // line( 8h, 8h,20h, 8h)
    20 08 20 20 // line(20h, 8h,20h,20h)
    20 20 08 20 // line(20h,20h, 8h,20h)
    08 20 08 08 // line( 8h,20h, 8h, 8h)
    08 08       // not enough points -> end of string
    

    [Edit1] raster image to string conversion

    First you need to convert your image to vector form. There are many sophistikated approaches to polygonize raster image to vector form but they are usually using advanced things from math,image processing,structures etc... requiring extensive knowledge on the subject. As I assume you need this just to visualize something on the device so Instead I would use very simple conversion resulting in unreasonable big results (in comparison to the advanced approaches). If your device does not have too small limit on the string size then you should be fine otherwise you would need use something more advanced then this:

    1. clear your vector representation

      list of points and list of lines

    2. loop through all horizontal lines of image

    3. process each line

      1. find first set pixel from current position x0
      2. find first unset pixel from current position x1
      3. if x0,x1 found then

        • add point (x0,y)
        • add line (x0,y,x1-1,y)
    4. after whole image processed convert vector form to string

      1. clear string
      2. add list of all points to string
      3. add separator * to string
      4. add list of all lines to string

    This is how it looks like in C++:

    // load input 2D BW (binary) image
    backbuffer in;
    in.bmp->LoadFromFile("in.bmp");
    in.resize(in.bmp->Width,in.bmp->Height);
    
    int x0,x1,x,y;
    // clear signature vecor represenytation
    gSigPoints.num=0;
    gSigLines.num=0;
    for (y=0;y<in.ys;y++)
     for (x=0;x<in.xs;)
        {
        for (;(x<in.xs)&&(!in.pyx[y][x]);x++); x0=x;    // find start of V-line
        for (;(x<in.xs)&&( in.pyx[y][x]);x++)  x1=x;    // find end of V-line
        if (x0<in.xs)                                   // add pnt,line to signature
            {
            gSigPoints.add(x0);
            gSigPoints.add(y );
            gSigLines.add(x0);
            gSigLines.add(y );
            gSigLines.add(x1);
            gSigLines.add(y );
            }
        }
    // update string and screen
    txt=PackBMP();
    draw();
    
    • where in.xs,in.ys is input image resolution
    • in.pyx[y][x] is the image pixel access
    • txt is the signature string
    • gSigPoints,gSigLines are lists holding the signature points and lines
    • these list have .num the number of items and .add(a) adds a to the end of the list

    The pack/unpack ported from your VB code looks like this:

    //---------------------------------------------------------------------------
    #include "list.h"
    //---------------------------------------------------------------------------
    List<DWORD> gSigPoints;
    List<DWORD> gSigLines;
    // some test examples:
    //AnsiString txt="221A*221A270A270A2503250320072007171617160D2A0D2A07380738073F073F0B3E0B3E15311531222122212C182C183016301631173117311A311A302230222E272E272D2C2D2C2C2F2C2F2C312C312C2F2C2F2E2B2E2B3126312633223322371D371D381C381C3B1B3B1B3C1C3C1C3D1F3D1F3D243D243C2D3C2D3A333A333A363A36393939393B383B383D363D36412E412E46264626492049204B1B4B1B4E184E184F174F175017501751185118511D511D51225122502450244F294F294F2C4F2C4E2F4E2F4F314F315030503052305230552C552C582958295D225D22601E601E611C611C621B621B621A621A601A601A5D1B5D1B5B1E5B1E5723572353285328512B512B502E502E502F502F513151315231523154305430582E582E592D592D5C2C5C2C5F2A5F2A6428642865286528692669266D246D247122712275207520791D791D7D1B7D1B81198119841884188618861887198719881A881A891B891B881C881C871E871E861F861F852085208421842182228222812481247F257F257E257E257D257D257C247C247C217C217D1F7D1F7E1C7E1C801A801A81198119821782178317831784188418851A851A851C851C86208620862286228625862587278727872B872B882E882E893289328A338A338E348E349033903393329332972F972F9A2D9A2D9F299F29A426A426AB20AB20AF1CAF1CB517B517B716B716B716";
    //AnsiString txt="3711*371127152715103510351F2C1F2C312231223C1C3C1C3D203D20352D352D333233323D2E3D2E52225222671A671A6C196C196D1A6D1A6D1D6D1D69226922652665266428642864296429652965296E236E23781E781E8718871891179117961896189A199A199B1B9B1B9D1E9D1E9F209F20A021A021A021";
    AnsiString txt="0808*08082008200820202020082008200808";
    //---------------------------------------------------------------------------
    AnsiString Hex(DWORD x,DWORD digits)
        {
        int i;
        char *tab="0123456789ABCDEF";
        AnsiString s="";
        if (digits>8) digits=8;
        s.SetLength(digits);
        for (i=digits;i>0;i--,x>>=4) s[i]=tab[x&15];
        return s;
        }
    //---------------------------------------------------------------------------
    AnsiString PackBMP()
        {
        DWORD i;
        AnsiString sig="";
        // all points
        for (i=0;i+1<gSigPoints.num;)
            {
            sig+=Hex(gSigPoints[i],2); i++; // x
            sig+=Hex(gSigPoints[i],2); i++; // y
            }
        // separator
        sig+="*";
        // all lines
        for (i=0;i+3<gSigLines.num;i++)
            {
            sig+=Hex(gSigLines[i],2); i++;  // x0
            sig+=Hex(gSigLines[i],2); i++;  // y0
            sig+=Hex(gSigLines[i],2); i++;  // x1
            sig+=Hex(gSigLines[i],2); i++;  // y1
            }
        return sig;
        }
    //---------------------------------------------------------------------------
    void UnpackBMP(AnsiString &sig)
        {
        DWORD a,x,y;
        int i=1,l=sig.Length();
        // all points
        for(gSigPoints.num=0;(i+3<=l)&&(sig[i]!='*');)
            {
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10;        x =a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10; x<<=4; x|=a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10;        y =a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10; y<<=4; y|=a; i++;
            gSigPoints.add(x);
            gSigPoints.add(y);
            }
        // separator
        i++;
        // all lines
        for(gSigLines.num=0;i+7<=l;)
            {
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10;        x =a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10; x<<=4; x|=a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10;        y =a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10; y<<=4; y|=a; i++;
            gSigLines.add(x);
            gSigLines.add(y);
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10;        x =a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10; x<<=4; x|=a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10;        y =a; i++;
            a=sig[i]-'0'; if (a>9) a+='0'-'A'+10; y<<=4; y|=a; i++;
            gSigLines.add(x);
            gSigLines.add(y);
            }
        }
    //---------------------------------------------------------------------------
    void DrawBMP(TCanvas *can)
        {
        DWORD i,x,y;
        // all points
        for (i=0;i+1<gSigPoints.num;)
            {
            x=gSigPoints[i]; i++;
            y=gSigPoints[i]; i++;
            can->Pixels[x][y]=can->Pen->Color;
            }
        // all lines
        for (i=0;i+3<gSigLines.num;)
            {
            x=gSigLines[i]; i++;
            y=gSigLines[i]; i++;
            can->MoveTo(x,y);
            x=gSigLines[i]; i++;
            y=gSigLines[i]; i++;
            can->LineTo(x,y);
            }
        }
    //---------------------------------------------------------------------------
    void signature_on_mouse(backbuffer &scr)
        {
        DWORD x,y;
        // mouse left button last and actual
        bool q0=scr.sh0.Contains(ssLeft);
        bool q1=scr.sh1.Contains(ssLeft);
        bool _redraw=false;
        // actual mouse position
        x=scr.mx1;
        y=scr.my1;
        // on mouse down event
        if ((!q0)&&(q1))
            {
            gSigPoints.add(x); gSigLines.add(x);
            gSigPoints.add(y); gSigLines.add(y);
            _redraw=true;
            }
        // on mouse move event
        if ((q0)&&(q1))
            {
            gSigLines.add(x);
            gSigLines.add(y);
            gSigLines.add(x);
            gSigLines.add(y);
            _redraw=true;
            }
        // mouse mouse up event
        if ((q0)&&(!q1))
            {
            gSigLines.add(x);
            gSigLines.add(y);
            _redraw=true;
            txt=PackBMP();
            }
        // right mouse button clears signature
        if (scr.sh1.Contains(ssRight))
            {
            gSigPoints.num=0;
            gSigLines.num=0;
            _redraw=true;
            }
        if ((_redraw)&&(scr.win)) scr.win->Repaint();
        scr.rfs_mouse();
        }
    //---------------------------------------------------------------------------
    
    • where backbuffer is mine class to interface the window backbuffer image and mouse handler events.
    • So just change List<>,AnsiString,Backbuffer to your platform style.

    This can handle any raster binary image not just signatures:

    But as you can see the size of signature string is ~ 13.9 KByte

    0 讨论(0)
提交回复
热议问题