Commits

Anonymous committed 9e42170

Added PixelArray.compare().

  • Participants
  • Parent commits e20d258

Comments (0)

Files changed (7)

docs/ref/index.html

 </ul>
 <li><a href="pixelarray.html#pygame.PixelArray">pygame.PixelArray</a> - <font size=-1>pygame Object for direct pixel access of surfaces</font></li>
 <ul>
+<li><a href="pixelarray.html#PixelArray.compare">PixelArray.compare</a> - <font size=-1>Compares the PixelArray with another one.</font></li>
 <li><a href="pixelarray.html#PixelArray.extract">PixelArray.extract</a> - <font size=-1>Extracts the passed color from the PixelArray.</font></li>
 <li><a href="pixelarray.html#PixelArray.make_surface">PixelArray.make_surface</a> - <font size=-1>Creates a new Surface from the current PixelArray.</font></li>
 <li><a href="pixelarray.html#PixelArray.replace">PixelArray.replace</a> - <font size=-1>Replaces the passed color in the PixelArray with another one.</font></li>

docs/ref/pixelarray.html

   <tr><td><a href="pixelarray.html#PixelArray.make_surface">PixelArray.make_surface</a> - <font size=-1>Creates a new Surface from the current PixelArray.</font></td><td>Creates a new Surface from the current PixelArray.</td></tr>
   <tr><td><a href="pixelarray.html#PixelArray.replace">PixelArray.replace</a> - <font size=-1>Replaces the passed color in the PixelArray with another one.</font></td><td>Replaces the passed color in the PixelArray with another one.</td></tr>
   <tr><td><a href="pixelarray.html#PixelArray.extract">PixelArray.extract</a> - <font size=-1>Extracts the passed color from the PixelArray.</font></td><td>Extracts the passed color from the PixelArray.</td></tr>
+  <tr><td><a href="pixelarray.html#PixelArray.compare">PixelArray.compare</a> - <font size=-1>Compares the PixelArray with another one.</font></td><td>Compares the PixelArray with another one.</td></tr>
 </table></small></ul>
 <p>The PixelArray wraps up a Surface and provides a direct <tt>2D</tt> array access to its pixels using the surface its rows as first and its columns as second axis. It supports slicing, row and pixel manipluation, slicing and slice assignments while inplace operations such as addition, subtraction, multiplication, division and so forth are not allowed. </p>
 <p>While it is possible to assign both, integer color values and <tt>RGB(A)</tt> color tuples, the PixelArray will only use integers for the color representation. Thus, checking for certain colors has to be done using the <tt><a href="surface.html#Surface.map_rgb">Surface.map_rgb</a> - <font size=-1>convert a color into a mapped color value</font></tt> method of the surface, the PixelArray was created for. </p>
 <p>Creates a new Surface from the current PixelArray. Depending on the current PixelArray the size, pixel order etc. will be different from the original Surface. </p>
 <pre>  # Create a new surface flipped around the vertical axis.
   sf = pxarray[:,::-1].make_surface ()
-</pre><p>New in <tt>pygame.1.8.1</tt>. </p>
+</pre><p>New in pygame <tt>1.8.1</tt>. </p>
 <!--COMMENTS:PixelArray.make_surface--> &nbsp;<br> 
 <br></ul>
 
   <i>Replaces the passed color in the PixelArray with another one.</i><br>
   <tt>PixelArray.replace (color, repcolor, distance=0, weights=(0.299, 0.587, 0.114)): Return None</tt><br>
 <p>Replaces the pixels with the passed color in the PixelArray by changing them them to the passed replacement color. </p>
-<p>It uses a simple weighted euclidian distance formula to calculate the distance between the colors. The distance space ranges from <tt>0.0</tt> to <tt>1.0</tt> and is used as threshold for the color detection. This causes the replacement to take pixels with a similar but not exactly identical color, into account as well. </p>
+<p>It uses a simple weighted euclidian distance formula to calculate the distance between the colors. The distance space ranges from <tt>0.0</tt> to <tt>1.0</tt> and is used as threshold for the color detection. This causes the replacement to take pixels with a similar, but not exactly identical color, into account as well. </p>
 <p>This is an in place operation that directly affects the pixels of the PixelArray. </p>
-<p>New in <tt>pygame.1.8.1</tt>. </p>
+<p>New in pygame <tt>1.8.1</tt>. </p>
 <!--COMMENTS:PixelArray.replace--> &nbsp;<br> 
 <br></ul>
 
   <i>Extracts the passed color from the PixelArray.</i><br>
   <tt>PixelArray.extract (color, distance=0, weights=(0.299, 0.587, 0.114)): Return PixelArray</tt><br>
 <p>Extracts the passed color by changing all matching pixels to white, while non-matching pixels are changed to black. This returns a new PixelArray with the black/white color mask. </p>
-<p>It uses a simple weighted euclidian distance formula to calculate the distance between the colors. The distance space ranges from <tt>0.0</tt> to <tt>1.0</tt> and is used as threshold for the color detection. This causes the extraction to take pixels with a similar but not exactly identical color, into account as well. </p>
-<p>New in <tt>pygame.1.8.1</tt>. </p>
+<p>It uses a simple weighted euclidian distance formula to calculate the distance between the colors. The distance space ranges from <tt>0.0</tt> to <tt>1.0</tt> and is used as threshold for the color detection. This causes the extraction to take pixels with a similar, but not exactly identical color, into account as well. </p>
+<p>New in pygame <tt>1.8.1</tt>. </p>
 <!--COMMENTS:PixelArray.extract--> &nbsp;<br> 
 <br></ul>
+
+
+<a name="PixelArray.compare">
+<big><b>PixelArray.compare</big></b><br><ul>
+  <i>Compares the PixelArray with another one.</i><br>
+  <tt>PixelArray.compare (array, distance=0, weights=(0.299, 0.587, 0.114)): Return PixelArray</tt><br>
+<p>Compares the contents of the PixelArray with those from the passed PixelArray. It returns a new PixelArray with a black/white color mask that indicates the differences (white) of both arrays. Both PixelArray objects must have indentical bit depths and dimensions. </p>
+<p>It uses a simple weighted euclidian distance formula to calculate the distance between the colors. The distance space ranges from <tt>0.0</tt> to <tt>1.0</tt> and is used as threshold for the color detection. This causes the comparision to mark pixels with a similar, but not exactly identical color, as black. </p>
+<p>New in pygame <tt>1.8.1</tt>. </p>
+<!--COMMENTS:PixelArray.compare--> &nbsp;<br> 
+<br></ul>
 <br></ul>
 
 </body></html>

examples/pixelarray.py

 sf2 = ar2.surface
 del ar, ar2
 show (sf2)
+
+# Compare two images.
+surface = pygame.image.load (os.path.join ('data', 'alien1.gif'))
+surface2 = pygame.image.load (os.path.join ('data', 'alien2.gif'))
+ar1 = pygame.PixelArray (surface)
+ar2 = pygame.PixelArray (surface2)
+ar3 = ar1.compare (ar2, 0.07)
+sf3 = ar3.surface
+del ar1, ar2, ar3
+show (sf3)
 /* C API interfaces */
 static PyObject* PyPixelArray_New (PyObject *surfobj);
 
+/* Incomplete forward declaration so we can use it in the methods included
+ * below.
+ */
+static PyTypeObject PyPixelArray_Type;
+#define PyPixelArray_Check(o) \
+    ((o)->ob_type == (PyTypeObject *) &PyPixelArray_Type)
+#define SURFACE_EQUALS(x,y) \
+    (((PyPixelArray *)x)->surface == ((PyPixelArray *)y)->surface)
+
 #include "pixelarray_methods.c"
 
 /**
  */
 static PyMethodDef _pxarray_methods[] =
 {
-    { "replace", (PyCFunction) _replace_color, METH_KEYWORDS,
-      DOC_PIXELARRAYREPLACE },
+    { "compare", (PyCFunction) _compare, METH_KEYWORDS,
+      DOC_PIXELARRAYCOMPARE },
     { "extract", (PyCFunction) _extract_color, METH_KEYWORDS,
       DOC_PIXELARRAYEXTRACT },
     { "make_surface", (PyCFunction) _make_surface, METH_NOARGS,
       DOC_PIXELARRAYMAKESURFACE },
+    { "replace", (PyCFunction) _replace_color, METH_KEYWORDS,
+      DOC_PIXELARRAYREPLACE },
     { NULL, NULL, 0, NULL }
 };
 
     0                           /* tp_del */
 };
 
-#define PyPixelArray_Check(o) \
-    ((o)->ob_type == (PyTypeObject *) &PyPixelArray_Type)
-#define SURFACE_EQUALS(x,y) \
-    (((PyPixelArray *)x)->surface == ((PyPixelArray *)y)->surface)
-
 static PyPixelArray*
 _pxarray_new_internal (PyTypeObject *type, PyObject *surface,
     Uint32 xstart, Uint32 ystart, Uint32 xlen, Uint32 ylen,

src/pixelarray.doc

   # Create a new surface flipped around the vertical axis.
   sf = pxarray[:,::-1].make_surface () 
 
-New in pygame.1.8.1.
+New in pygame 1.8.1.
 <END>
 
 replace
 It uses a simple weighted euclidian distance formula to calculate the
 distance between the colors. The distance space ranges from 0.0 to 1.0
 and is used as threshold for the color detection. This causes the
-replacement to take pixels with a similar but not exactly identical
+replacement to take pixels with a similar, but not exactly identical
 color, into account as well.
 
 This is an in place operation that directly affects the pixels of the
 PixelArray.
 
-New in pygame.1.8.1.
+New in pygame 1.8.1.
 <END>
 
 extract
 It uses a simple weighted euclidian distance formula to calculate the
 distance between the colors.  The distance space ranges from 0.0 to
 1.0 and is used as threshold for the color detection. This causes the
-extraction to take pixels with a similar but not exactly identical
+extraction to take pixels with a similar, but not exactly identical
 color, into account as well.
 
-New in pygame.1.8.1.
+New in pygame 1.8.1.
 <END>
+
+compare
+Compares the PixelArray with another one.
+PixelArray.compare (array, distance=0, weights=(0.299, 0.587, 0.114)): Return PixelArray
+
+Compares the contents of the PixelArray with those from the passed PixelArray.
+It returns a new PixelArray with a black/white color mask that indicates the
+differences (white) of both arrays. Both PixelArray objects must have indentical
+bit depths and dimensions.
+
+It uses a simple weighted euclidian distance formula to calculate the
+distance between the colors.  The distance space ranges from 0.0 to
+1.0 and is used as threshold for the color detection. This causes the
+comparision to mark pixels with a similar, but not exactly identical
+color, as black.
+
+New in pygame 1.8.1.
 <END>
+<END>

src/pixelarray_methods.c

         return RAISE (PyExc_SDLError, SDL_GetError ());
     }
     SDL_FreeSurface (tmpsf);
+    
+    newsf = PySurface_New (newsurf);
+    if (!newsf)
+    {
+        SDL_FreeSurface (newsurf);
+        return NULL;
+    }
 
     /* Acquire a temporary lock. */
     if (SDL_MUSTLOCK (newsurf) == 0)
 
     if (SDL_MUSTLOCK (newsurf) == 0)
         SDL_UnlockSurface (newsurf);
-    newsf = PySurface_New (newsurf);
-    if (!newsf)
-        return NULL;
     return newsf;
 }
 
     if (distance < 0 || distance > 1)
         return RAISE (PyExc_ValueError,
             "distance must be in the range from 0.0 to 1.0");
+    if (!_get_weights (weights, &wr, &wg, &wb))
+        return NULL;
 
     surface = PySurface_AsSurface (array->surface);
     if (!_get_color_from_object (excolor, surface->format, &color))
         return NULL;
 
-    if (!_get_weights (weights, &wr, &wg, &wb))
-        return NULL;
-
     /* Create the b/w mask surface. */
     sf = _make_surface (array);
     if (!sf)
         return NULL;
     newarray = (PyPixelArray *) PyPixelArray_New (sf);
     if (!newarray)
+    {
+        Py_DECREF (sf);
         return NULL;
+    }
     surface = PySurface_AsSurface (newarray->surface);
 
     black = SDL_MapRGBA (surface->format, 0, 0, 0, 255);
     case 1:
     {
         Uint8 *pixel;
-        while (posy < array->ylen)
+        while (posy < newarray->ylen)
         {
             x = newarray->xstart;
             posx = 0;
         Uint16 *pixel;
         while (posy < newarray->ylen)
         {
-            x = array->xstart;
+            x = newarray->xstart;
             posx = 0;
             while (posx < newarray->xlen)
             {
     Py_END_ALLOW_THREADS;
     return (PyObject *) newarray;
 }
+
+static PyObject*
+_compare (PyPixelArray *array, PyObject *args, PyObject *kwds)
+{
+    PyPixelArray *array2 = NULL;
+    PyPixelArray *newarray = NULL;
+    PyObject *weights = NULL;
+    PyObject *sf = NULL;
+    SDL_Surface *surface1 = NULL;
+    SDL_Surface *surface2 = NULL;
+    Uint32 black;
+    Uint32 white;
+    float distance = 0;
+    Uint8 r1, g1, b1, a1, r2, g2, b2, a2;
+    float wr, wg, wb;
+
+    Uint32 x = 0;
+    Uint32 y = 0;
+    Uint32 vx = 0;
+    Uint32 vy = 0;
+    Uint32 posx = 0;
+    Uint32 posy = 0;
+    Sint32 absxstep;
+    Sint32 absystep;
+    Uint8 *pixels1;
+    Uint8 *pixels2;
+
+    static char *keys[] = { "array", "distance", "weights", NULL };
+
+    if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|fO", keys, &newarray,
+            &distance, &weights))
+        return NULL;
+
+    if (!PyPixelArray_Check (newarray))
+        return RAISE (PyExc_TypeError, "invalid array type");
+    array2 = (PyPixelArray *) newarray;
+    newarray = NULL;
+    if (distance < 0 || distance > 1)
+        return RAISE (PyExc_ValueError,
+            "distance must be in the range from 0.0 to 1.0");
+    if (!_get_weights (weights, &wr, &wg, &wb))
+        return NULL;
+
+    if (array->ylen / ABS (array->ystep) != array2->ylen / ABS (array2->ystep)
+       || array->xlen / ABS (array->xstep) != array2->xlen / ABS (array2->xstep))
+    {
+        /* Bounds do not match. */
+        PyErr_SetString (PyExc_ValueError, "array sizes do not match");
+        return NULL;
+    }
+
+    surface1 = PySurface_AsSurface (array->surface);
+    surface2 = PySurface_AsSurface (array2->surface);
+    if (surface2->format->BytesPerPixel != surface1->format->BytesPerPixel)
+        return RAISE (PyExc_ValueError, "bit depths do not match");
+
+    /* Create the b/w mask surface. */
+    sf = _make_surface (array);
+    if (!sf)
+        return NULL;
+    newarray = (PyPixelArray *) PyPixelArray_New (sf);
+    if (!newarray)
+    {
+        Py_DECREF (sf);
+        return NULL;
+    }
+    surface1 = PySurface_AsSurface (newarray->surface);
+    
+    black = SDL_MapRGBA (surface1->format, 0, 0, 0, 255);
+    white = SDL_MapRGBA (surface1->format, 255, 255, 255, 255);
+
+    pixels1 = surface1->pixels;
+    pixels2 = surface2->pixels;
+    absxstep = ABS (array2->xstep);
+    absystep = ABS (array2->ystep);
+    y = array2->ystart;
+
+    Py_BEGIN_ALLOW_THREADS;
+    switch (surface1->format->BytesPerPixel)
+    {
+    case 1:
+    {
+        Uint8 *pixel1, *pixel2;
+        while (posy < newarray->ylen)
+        {
+            vx = array2->xstart;
+            x = newarray->xstart;
+            posx = 0;
+            while (posx < newarray->xlen)
+            {
+                pixel1 = ((Uint8 *) pixels1 + y * surface1->pitch + x);
+                pixel2 = ((Uint8 *) pixels2 + vy * surface2->pitch + vx);
+                if (distance)
+                {
+                    GET_PIXELVALS_1 (r1, g1, b1, a1, pixel1, surface1->format);
+                    GET_PIXELVALS_1 (r2, g2, b2, a2, pixel2, surface2->format);
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) >
+                        distance)
+                        *pixel1 = (Uint8) white;
+                    else
+                        *pixel1 = (Uint8) black;
+                }
+                else
+                    *pixel1 = (*pixel1 == *pixel2) ? (Uint8) white :
+                        (Uint8) black;
+                vx += array2->xstep;
+                x += newarray->xstep;
+                posx += absxstep;
+            }
+            vy += array2->ystep;
+            y += newarray->ystep;
+            posy += absystep;
+        }
+        break;
+    }
+    case 2:
+    {
+        Uint16 *pixel1, *pixel2;
+        while (posy < newarray->ylen)
+        {
+            vx = array2->xstart;
+            x = array->xstart;
+            posx = 0;
+            while (posx < newarray->xlen)
+            {
+                pixel1 = ((Uint16 *) (pixels1 + y * surface1->pitch) + x);
+                pixel2 = ((Uint16 *) (pixels2 + vy * surface2->pitch) + vx);
+                if (distance)
+                {
+                    GET_PIXELVALS (r1, g1, b1, a1, (Uint32) *pixel1,
+                        surface1->format);
+                    GET_PIXELVALS (r2, g2, b2, a2, (Uint32) *pixel2,
+                        surface1->format);
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) >
+                        distance)
+                        *pixel1 = (Uint16) white;
+                    else
+                        *pixel1 = (Uint16) black;
+                }
+                else
+                    *pixel1 = (*pixel1 == *pixel2) ? (Uint16) white :
+                        (Uint16) black;
+                vx += array2->xstep;
+                x += newarray->xstep;
+                posx += absxstep;
+            }
+            vy += array2->ystep;
+            y += newarray->ystep;
+            posy += absystep;
+        }
+        break;
+    }
+    case 3:
+    {
+        Uint8 *px1, *px2;
+        Uint32 pxcolor1, pxcolor2;
+        SDL_PixelFormat *format = surface1->format;
+        while (posy < newarray->ylen)
+        {
+            vx = array2->xstart;
+            x = newarray->xstart;
+            posx = 0;
+            while (posx < newarray->xlen)
+            {
+                px1 = (Uint8 *) (pixels1 + y * surface1->pitch) + x * 3;
+                px2 = (Uint8 *) (pixels2 + vy * surface2->pitch) + vx * 3;
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                pxcolor1 = (px1[0]) + (px1[1] << 8) + (px1[2] << 16);
+                pxcolor2 = (px2[0]) + (px2[1] << 8) + (px2[2] << 16);
+                if (distance)
+                {
+                    GET_PIXELVALS (r1, g1, b1, a1, pxcolor1, surface1->format);
+                    GET_PIXELVALS (r2, g2, b2, a2, pxcolor2, surface2->format);
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) >
+                        distance)
+                    {
+                        *(px1 + (format->Rshift >> 3)) = (Uint8) (white >> 16);
+                        *(px1 + (format->Gshift >> 3)) = (Uint8) (white >> 8);
+                        *(px1 + (format->Bshift >> 3)) = (Uint8) white;
+                    }
+                    else
+                    {
+                        *(px1 + (format->Rshift >> 3)) = (Uint8) (black >> 16);
+                        *(px1 + (format->Gshift >> 3)) = (Uint8) (black >> 8);
+                        *(px1 + (format->Bshift >> 3)) = (Uint8) black;
+                    }
+                }
+                else if (pxcolor1 != pxcolor2)
+                {
+                    *(px1 + (format->Rshift >> 3)) = (Uint8) (white >> 16);
+                    *(px1 + (format->Gshift >> 3)) = (Uint8) (white >> 8);
+                    *(px1 + (format->Bshift >> 3)) = (Uint8) white;
+                }
+                else
+                {
+                    *(px1 + (format->Rshift >> 3)) = (Uint8) (black >> 16);
+                    *(px1 + (format->Gshift >> 3)) = (Uint8) (black >> 8);
+                    *(px1 + (format->Bshift >> 3)) = (Uint8) black;
+                }
+#else
+                pxcolor1 = (px1[2]) + (px1[1] << 8) + (px1[0] << 16);
+                pxcolor2 = (px2[2]) + (px2[1] << 8) + (px2[0] << 16);
+                if (distance)
+                {
+                    GET_PIXELVALS (r1, g1, b1, a1, pxcolor1, surface1->format);
+                    GET_PIXELVALS (r2, g2, b2, a2, pxcolor2, surface2->format);
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) >
+                        distance)
+                    {
+                        *(px1 + 2 - (format->Rshift >> 3)) =
+                            (Uint8) (white >> 16);
+                        *(px1 + 2 - (format->Gshift >> 3)) =
+                            (Uint8) (white >> 8);
+                        *(px1 + 2 - (format->Bshift >> 3)) = (Uint8) white;
+                    }
+                    else
+                    {
+                        *(px1 + 2 - (format->Rshift >> 3)) =
+                            (Uint8) (black >> 16);
+                        *(px1 + 2 - (format->Gshift >> 3)) =
+                            (Uint8) (black >> 8);
+                        *(px1 + 2 - (format->Bshift >> 3)) = (Uint8) black;
+                    }
+                }
+                else if (pxcolor1 != pxcolor2)
+                {
+                    *(px1 + 2 - (format->Rshift >> 3)) = (Uint8) (white >> 16);
+                    *(px1 + 2 - (format->Gshift >> 3)) = (Uint8) (white >> 8);
+                    *(px1 + 2 - (format->Bshift >> 3)) = (Uint8) white;
+                }
+                else
+                {
+                    *(px1 + 2 - (format->Rshift >> 3)) = (Uint8) (black >> 16);
+                    *(px1 + 2 - (format->Gshift >> 3)) = (Uint8) (black >> 8);
+                    *(px1 + 2 - (format->Bshift >> 3)) = (Uint8) black;
+                }
+#endif
+                vx += array2->xstep;
+                x += newarray->xstep;
+                posx += absxstep;
+            }
+            vy += array2->ystep;
+            y += newarray->ystep;
+            posy += absystep;
+        }
+        break;
+    }
+    default:
+    {
+        Uint32 *pixel1, *pixel2;
+        while (posy < newarray->ylen)
+        {
+            vx = array2->xstart;
+            x = newarray->xstart;
+            posx = 0;
+            while (posx < newarray->xlen)
+            {
+                pixel1 = ((Uint32 *) (pixels1 + y * surface1->pitch) + x);
+                pixel2 = ((Uint32 *) (pixels2 + vy * surface2->pitch) + vx);
+                if (distance)
+                {
+                    GET_PIXELVALS (r1, g1, b1, a1, *pixel1, surface1->format);
+                    GET_PIXELVALS (r2, g2, b2, a2, *pixel2, surface2->format);
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) >
+                        distance)
+                        *pixel1 = white;
+                    else
+                        *pixel1 = black;
+                }
+                else
+                    *pixel1 = (*pixel1 == *pixel2) ? white : black;
+                vx += array2->xstep;
+                x += newarray->xstep;
+                posx += absxstep;
+            }
+            vy += array2->ystep;
+            y += newarray->ystep;
+            posy += absystep;
+        }
+        break;
+    }
+    }
+    Py_END_ALLOW_THREADS;
+    return (PyObject *) newarray;
+}

test/pixelarray_test.py

             self.assertEqual (iterations, 5)
 
     def test_replace (self):
+        #print "replace start"
         for bpp in (8, 16, 24, 32):
             sf = pygame.Surface ((10, 10), 0, bpp)
             sf.fill ((255, 0, 0))
             self.assertEqual (ar[3][6], oval)
             self.assertEqual (ar[8][9], oval)
             self.assertEqual (ar[9][9], oval)
+        #print "replace end"
 
     def test_extract (self):
+        #print "extract start"
         for bpp in (8, 16, 24, 32):
             sf = pygame.Surface ((10, 10), 0, bpp)
             sf.fill ((0, 0, 255))
             self.assertEqual (newar[3][6], white)
             self.assertEqual (newar[8][9], black)
             self.assertEqual (newar[9][9], black)
-
+        #print "extract end"
 if __name__ == '__main__':
     unittest.main()