src/affine.c
PIX *
pixAffineSequential(PIX     *pixs,
                    PTA     *ptad,
                    PTA     *ptas,
                    l_int32  bw,
                    l_int32  bh)
{
l_int32    x1, y1, x2, y2, x3, y3;    /* ptas */
l_int32    x1p, y1p, x2p, y2p, x3p, y3p;   /* ptad */
l_int32    x1sc, y1sc;  /* scaled origin */
l_float32  x2s, x2sp, scalex, scaley;
l_float32  th3, th3p, ph2, ph2p;
l_float32  rad2deg;
PIX       *pixt1, *pixt2, *pixd;

    PROCNAME("pixAffineSequential");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (!ptas)
        return (PIX *)ERROR_PTR("ptas not defined", procName, NULL);
    if (!ptad)
        return (PIX *)ERROR_PTR("ptad not defined", procName, NULL);

    if (ptaGetCount(ptas) != 3)
        return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL);
    if (ptaGetCount(ptad) != 3)
        return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL);
    ptaGetIPt(ptas, 0, &x1, &y1);
    ptaGetIPt(ptas, 1, &x2, &y2);
    ptaGetIPt(ptas, 2, &x3, &y3);
    ptaGetIPt(ptad, 0, &x1p, &y1p);
    ptaGetIPt(ptad, 1, &x2p, &y2p);
    ptaGetIPt(ptad, 2, &x3p, &y3p);

    rad2deg = 180. / 3.1415926535;

    if (y1 == y3)
        return (PIX *)ERROR_PTR("y1 == y3!", procName, NULL);
    if (y1p == y3p)
        return (PIX *)ERROR_PTR("y1p == y3p!", procName, NULL);
        
    if (bw != 0 || bh != 0) {
            /* resize all points and add border to pixs */
        x1 = x1 + bw;
        y1 = y1 + bh;
        x2 = x2 + bw;
        y2 = y2 + bh;
        x3 = x3 + bw;
        y3 = y3 + bh;
        x1p = x1p + bw;
        y1p = y1p + bh;
        x2p = x2p + bw;
        y2p = y2p + bh;
        x3p = x3p + bw;
        y3p = y3p + bh;

        if ((pixt1 = pixAddBorderGeneral(pixs, bw, bw, bh, bh, 0)) == NULL)
            return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL);
    }
    else
        pixt1 = pixCopy(NULL, pixs);

    /*-------------------------------------------------------------*
        The horizontal shear is done to move the 3rd point to the
        y axis.  This moves the 2nd point either towards or away
        from the y axis, depending on whether it is above or below
        the x axis.  That motion must be computed so that we know
        the angle of vertical shear to use to get the 2nd point
        on the x axis.  We must also know the x coordinate of the
        2nd point in order to compute how much scaling is required
        to match points on the axis.
     *-------------------------------------------------------------*/

        /* Shear angles required to put src points on x and y axes */
    th3 = atan2((l_float64)(x1 - x3), (l_float64)(y1 - y3));
    x2s = (l_float32)(x2 - ((l_float32)(y1 - y2) * (x3 - x1)) / (y1 - y3));
    if (x2s == (l_float32)x1)
        return (PIX *)ERROR_PTR("x2s == x1!", procName, NULL);
    ph2 = atan2((l_float64)(y1 - y2), (l_float64)(x2s - x1));

        /* Shear angles required to put dest points on x and y axes.
         * Use the negative of these values to instead move the
         * src points from the axes to the actual dest position.
         * These values are also needed to scale the image. */
    th3p = atan2((l_float64)(x1p - x3p), (l_float64)(y1p - y3p));
    x2sp = (l_float32)(x2p - ((l_float32)(y1p - y2p) * (x3p - x1p)) / (y1p - y3p));
    if (x2sp == (l_float32)x1p)
        return (PIX *)ERROR_PTR("x2sp == x1p!", procName, NULL);
    ph2p = atan2((l_float64)(y1p - y2p), (l_float64)(x2sp - x1p));

        /* Shear image to first put src point 3 on the y axis,
         * and then to put src point 2 on the x axis */
    pixHShearIP(pixt1, y1, th3, L_BRING_IN_WHITE);
    pixVShearIP(pixt1, x1, ph2, L_BRING_IN_WHITE);

        /* Scale image to match dest scale.  The dest scale
         * is calculated above from the angles th3p and ph2p
         * that would be required to move the dest points to
         * the x and y axes. */
    scalex = (l_float32)(x2sp - x1p) / (x2s - x1);
    scaley = (l_float32)(y3p - y1p) / (y3 - y1);
    if ((pixt2 = pixScale(pixt1, scalex, scaley)) == NULL)
        return (PIX *)ERROR_PTR("pixt2 not made", procName, NULL);

#if  DEBUG
    fprintf(stderr, "th3 = %5.1f deg, ph2 = %5.1f deg\n",
            rad2deg * th3, rad2deg * ph2);
    fprintf(stderr, "th3' = %5.1f deg, ph2' = %5.1f deg\n",
            rad2deg * th3p, rad2deg * ph2p);
    fprintf(stderr, "scalex = %6.3f, scaley = %6.3f\n", scalex, scaley);
#endif  /* DEBUG */

    /*-------------------------------------------------------------*
        Scaling moves the 1st src point, which is the origin. 
        It must now be moved again to coincide with the origin
        (1st point) of the dest.  After this is done, the 2nd
        and 3rd points must be sheared back to the original
        positions of the 2nd and 3rd dest points.  We use the
        negative of the angles that were previously computed
        for shearing those points in the dest image to x and y
        axes, and take the shears in reverse order as well.
     *-------------------------------------------------------------*/
        /* Shift image to match dest origin. */
    x1sc = (l_int32)(scalex * x1 + 0.5);   /* x comp of origin after scaling */
    y1sc = (l_int32)(scaley * y1 + 0.5);   /* y comp of origin after scaling */
    pixRasteropIP(pixt2, x1p - x1sc, y1p - y1sc, L_BRING_IN_WHITE);

        /* Shear image to take points 2 and 3 off the axis and
         * put them in the original dest position */
    pixVShearIP(pixt2, x1p, -ph2p, L_BRING_IN_WHITE);
    pixHShearIP(pixt2, y1p, -th3p, L_BRING_IN_WHITE);

    if (bw != 0 || bh != 0) {
        if ((pixd = pixRemoveBorderGeneral(pixt2, bw, bw, bh, bh)) == NULL)
            return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    }
    else
        pixd = pixClone(pixt2);

    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    return pixd;
}
/*!
 *  pixAffineSequential()
 *
 *      Input:  pixs
 *              ptad  (3 pts of final coordinate space)
 *              ptas  (3 pts of initial coordinate space)
 *              bw    (pixels of additional border width during computation)
 *              bh    (pixels of additional border height during computation)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) The 3 pts must not be collinear.
 *      (2) The 3 pts must be given in this order:
 *           - origin
 *           - a location along the x-axis
 *           - a location along the y-axis.
 *      (3) You must guess how much border must be added so that no
 *          pixels are lost in the transformations from src to
 *          dest coordinate space.  (This can be calculated but it
 *          is a lot of work!)  For coordinate spaces that are nearly
 *          at right angles, on a 300 ppi scanned page, the addition
 *          of 1000 pixels on each side is usually sufficient.
 *      (4) This is here for pedagogical reasons.  It is about 3x faster
 *          on 1 bpp images than pixAffineSampled(), but the results
 *          on text are much inferior.
 */
pixAffineSequential()
Input:  pixs
ptad  (3 pts of final coordinate space)
ptas  (3 pts of initial coordinate space)
bw    (pixels of additional border width during computation)
bh    (pixels of additional border height during computation)
Return: pixd, or null on error

(1) The 3 pts must not be collinear.
pixAffineSequential () Input : pixs ptad ( 3 pts of final coordinate space ) ptas ( 3 pts of initial coordinate space ) bw ( pixels of additional border width during computation ) bh ( pixels of additional border height during computation ) Return : pixd or null on error ( 1 ) The 3 pts must not be collinear .
PIX *
pixAffineSequential(PIX     *pixs,
                    PTA     *ptad,
                    PTA     *ptas,
                    l_int32  bw,
                    l_int32  bh)
{
l_int32    x1, y1, x2, y2, x3, y3;    
l_int32    x1p, y1p, x2p, y2p, x3p, y3p;   
l_int32    x1sc, y1sc;  
l_float32  x2s, x2sp, scalex, scaley;
l_float32  th3, th3p, ph2, ph2p;
l_float32  rad2deg;
PIX       *pixt1, *pixt2, *pixd;
    PROCNAME("pixAffineSequential");
    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (!ptas)
        return (PIX *)ERROR_PTR("ptas not defined", procName, NULL);
    if (!ptad)
        return (PIX *)ERROR_PTR("ptad not defined", procName, NULL);
    if (ptaGetCount(ptas) != 3)
        return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL);
    if (ptaGetCount(ptad) != 3)
        return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL);
    ptaGetIPt(ptas, 0, &x1, &y1);
    ptaGetIPt(ptas, 1, &x2, &y2);
    ptaGetIPt(ptas, 2, &x3, &y3);
    ptaGetIPt(ptad, 0, &x1p, &y1p);
    ptaGetIPt(ptad, 1, &x2p, &y2p);
    ptaGetIPt(ptad, 2, &x3p, &y3p);
    rad2deg = 180. / 3.1415926535;
    if (y1 == y3)
        return (PIX *)ERROR_PTR("y1 == y3!", procName, NULL);
    if (y1p == y3p)
        return (PIX *)ERROR_PTR("y1p == y3p!", procName, NULL);
    if (bw != 0 || bh != 0) {
        x1 = x1 + bw;
        y1 = y1 + bh;
        x2 = x2 + bw;
        y2 = y2 + bh;
        x3 = x3 + bw;
        y3 = y3 + bh;
        x1p = x1p + bw;
        y1p = y1p + bh;
        x2p = x2p + bw;
        y2p = y2p + bh;
        x3p = x3p + bw;
        y3p = y3p + bh;
        if ((pixt1 = pixAddBorderGeneral(pixs, bw, bw, bh, bh, 0)) == NULL)
            return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL);
    }
    else
        pixt1 = pixCopy(NULL, pixs);
    th3 = atan2((l_float64)(x1 - x3), (l_float64)(y1 - y3));
    x2s = (l_float32)(x2 - ((l_float32)(y1 - y2) * (x3 - x1)) / (y1 - y3));
    if (x2s == (l_float32)x1)
        return (PIX *)ERROR_PTR("x2s == x1!", procName, NULL);
    ph2 = atan2((l_float64)(y1 - y2), (l_float64)(x2s - x1));
    th3p = atan2((l_float64)(x1p - x3p), (l_float64)(y1p - y3p));
    x2sp = (l_float32)(x2p - ((l_float32)(y1p - y2p) * (x3p - x1p)) / (y1p - y3p));
    if (x2sp == (l_float32)x1p)
        return (PIX *)ERROR_PTR("x2sp == x1p!", procName, NULL);
    ph2p = atan2((l_float64)(y1p - y2p), (l_float64)(x2sp - x1p));
    pixHShearIP(pixt1, y1, th3, L_BRING_IN_WHITE);
    pixVShearIP(pixt1, x1, ph2, L_BRING_IN_WHITE);
    scalex = (l_float32)(x2sp - x1p) / (x2s - x1);
    scaley = (l_float32)(y3p - y1p) / (y3 - y1);
    if ((pixt2 = pixScale(pixt1, scalex, scaley)) == NULL)
        return (PIX *)ERROR_PTR("pixt2 not made", procName, NULL);
#if  DEBUG
    fprintf(stderr, "th3 = %5.1f deg, ph2 = %5.1f deg\n",
            rad2deg * th3, rad2deg * ph2);
    fprintf(stderr, "th3' = %5.1f deg, ph2' = %5.1f deg\n",
            rad2deg * th3p, rad2deg * ph2p);
    fprintf(stderr, "scalex = %6.3f, scaley = %6.3f\n", scalex, scaley);
#endif  
    x1sc = (l_int32)(scalex * x1 + 0.5);   
    y1sc = (l_int32)(scaley * y1 + 0.5);   
    pixRasteropIP(pixt2, x1p - x1sc, y1p - y1sc, L_BRING_IN_WHITE);
    pixVShearIP(pixt2, x1p, -ph2p, L_BRING_IN_WHITE);
    pixHShearIP(pixt2, y1p, -th3p, L_BRING_IN_WHITE);
    if (bw != 0 || bh != 0) {
        if ((pixd = pixRemoveBorderGeneral(pixt2, bw, bw, bh, bh)) == NULL)
            return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    }
    else
        pixd = pixClone(pixt2);
    pixDestroy(&pixt1);
    pixDestroy(&pixt2);
    return pixd;
}
PIX * pixAffineSequential ( PIX * pixs , PTA * ptad , PTA * ptas , l_int32 bw , l_int32 bh ) { l_int32 x1 , y1 , x2 , y2 , x3 , y3 ; l_int32 x1p , y1p , x2p , y2p , x3p , y3p ; l_int32 x1sc , y1sc ; l_float32 x2s , x2sp , scalex , scaley ; l_float32 th3 , th3p , ph2 , ph2p ; l_float32 rad2deg ; PIX * pixt1 , * pixt2 , * pixd ; PROCNAME ( " " ) ; if ( ! pixs ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( ! ptas ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( ! ptad ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( ptaGetCount ( ptas ) != 3 ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( ptaGetCount ( ptad ) != 3 ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; ptaGetIPt ( ptas , 0 , & x1 , & y1 ) ; ptaGetIPt ( ptas , 1 , & x2 , & y2 ) ; ptaGetIPt ( ptas , 2 , & x3 , & y3 ) ; ptaGetIPt ( ptad , 0 , & x1p , & y1p ) ; ptaGetIPt ( ptad , 1 , & x2p , & y2p ) ; ptaGetIPt ( ptad , 2 , & x3p , & y3p ) ; rad2deg = 180. / 3.1415926535 ; if ( y1 == y3 ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( y1p == y3p ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( bw != 0 || bh != 0 ) { x1 = x1 + bw ; y1 = y1 + bh ; x2 = x2 + bw ; y2 = y2 + bh ; x3 = x3 + bw ; y3 = y3 + bh ; x1p = x1p + bw ; y1p = y1p + bh ; x2p = x2p + bw ; y2p = y2p + bh ; x3p = x3p + bw ; y3p = y3p + bh ; if ( ( pixt1 = pixAddBorderGeneral ( pixs , bw , bw , bh , bh , 0 ) ) == NULL ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; } else pixt1 = pixCopy ( NULL , pixs ) ; th3 = atan2 ( ( l_float64 ) ( x1 - x3 ) , ( l_float64 ) ( y1 - y3 ) ) ; x2s = ( l_float32 ) ( x2 - ( ( l_float32 ) ( y1 - y2 ) * ( x3 - x1 ) ) / ( y1 - y3 ) ) ; if ( x2s == ( l_float32 ) x1 ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; ph2 = atan2 ( ( l_float64 ) ( y1 - y2 ) , ( l_float64 ) ( x2s - x1 ) ) ; th3p = atan2 ( ( l_float64 ) ( x1p - x3p ) , ( l_float64 ) ( y1p - y3p ) ) ; x2sp = ( l_float32 ) ( x2p - ( ( l_float32 ) ( y1p - y2p ) * ( x3p - x1p ) ) / ( y1p - y3p ) ) ; if ( x2sp == ( l_float32 ) x1p ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; ph2p = atan2 ( ( l_float64 ) ( y1p - y2p ) , ( l_float64 ) ( x2sp - x1p ) ) ; pixHShearIP ( pixt1 , y1 , th3 , L_BRING_IN_WHITE ) ; pixVShearIP ( pixt1 , x1 , ph2 , L_BRING_IN_WHITE ) ; scalex = ( l_float32 ) ( x2sp - x1p ) / ( x2s - x1 ) ; scaley = ( l_float32 ) ( y3p - y1p ) / ( y3 - y1 ) ; if ( ( pixt2 = pixScale ( pixt1 , scalex , scaley ) ) == NULL ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; #if DEBUG fprintf ( stderr , " \n " , rad2deg * th3 , rad2deg * ph2 ) ; fprintf ( stderr , " \n " , rad2deg * th3p , rad2deg * ph2p ) ; fprintf ( stderr , " \n " , scalex , scaley ) ; #endif x1sc = ( l_int32 ) ( scalex * x1 + 0.5 ) ; y1sc = ( l_int32 ) ( scaley * y1 + 0.5 ) ; pixRasteropIP ( pixt2 , x1p - x1sc , y1p - y1sc , L_BRING_IN_WHITE ) ; pixVShearIP ( pixt2 , x1p , - ph2p , L_BRING_IN_WHITE ) ; pixHShearIP ( pixt2 , y1p , - th3p , L_BRING_IN_WHITE ) ; if ( bw != 0 || bh != 0 ) { if ( ( pixd = pixRemoveBorderGeneral ( pixt2 , bw , bw , bh , bh ) ) == NULL ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; } else pixd = pixClone ( pixt2 ) ; pixDestroy ( & pixt1 ) ; pixDestroy ( & pixt2 ) ; return pixd ; }
pixAffineSequential()
      Input:  pixs
              ptad  (3 pts of final coordinate space)
              ptas  (3 pts of initial coordinate space)
              bw    (pixels of additional border width during computation)
              bh    (pixels of additional border height during computation)
      Return: pixd, or null on error
pixAffineSequential () Input : pixs ptad ( 3 pts of final coordinate space ) ptas ( 3 pts of initial coordinate space ) bw ( pixels of additional border width during computation ) bh ( pixels of additional border height during computation ) Return : pixd or null on error
/* ptas */ /* ptad */ /* scaled origin */ /* resize all points and add border to pixs */ /*-------------------------------------------------------------*
        The horizontal shear is done to move the 3rd point to the
        y axis.  This moves the 2nd point either towards or away
        from the y axis, depending on whether it is above or below
        the x axis.  That motion must be computed so that we know
        the angle of vertical shear to use to get the 2nd point
        on the x axis.  We must also know the x coordinate of the
        2nd point in order to compute how much scaling is required
        to match points on the axis.
     *-------------------------------------------------------------*/ /* Shear angles required to put src points on x and y axes */ /* Shear angles required to put dest points on x and y axes.
         * Use the negative of these values to instead move the
         * src points from the axes to the actual dest position.
         * These values are also needed to scale the image. */ /* Shear image to first put src point 3 on the y axis,
         * and then to put src point 2 on the x axis */ /* Scale image to match dest scale.  The dest scale
         * is calculated above from the angles th3p and ph2p
         * that would be required to move the dest points to
         * the x and y axes. */ /* DEBUG */ /*-------------------------------------------------------------*
        Scaling moves the 1st src point, which is the origin. 
        It must now be moved again to coincide with the origin
        (1st point) of the dest.  After this is done, the 2nd
        and 3rd points must be sheared back to the original
        positions of the 2nd and 3rd dest points.  We use the
        negative of the angles that were previously computed
        for shearing those points in the dest image to x and y
        axes, and take the shears in reverse order as well.
     *-------------------------------------------------------------*/ /* Shift image to match dest origin. */ /* x comp of origin after scaling */ /* y comp of origin after scaling */ /* Shear image to take points 2 and 3 off the axis and
         * put them in the original dest position */