|
List Info
Thread: Comparison of PIL and GD speed for setting pixels through python
|
|
| Comparison of PIL and GD speed for
setting pixels through python |

|
2007-02-02 00:32:57 |
Hi,
I've had a need to do some optimisation of some low level
pixel setting
code, and have as such done some tests of different ways of
doing this
using PIL and alternatively with gd via ctypes.
Ctypes provides a fairly easy way to interface to gd, and
allows you to
easily use small sections of optimising C code. A summary
of the
results is below, but basically using ctyles/GD/c is about
20 or so
times faster than PIL and the new Image.load, and over 200
times faster
than using PIL's ImageDraw.point method.
Method Time (s) Times slower than fastest
+-----+ +-------+ +------------------------+
ctypes, c, GD 0.00181 1.0
PIL - 'load' 0.03965 21.9
ctypes, GD 0.18710 103.5
PIL - 'point' 0.45009 248.9
A full writeup of this can be found here :
http://www.lan
garson.com.au/blog/?p=10
I hope this is of use, and I look forward to comments,
particularly on
other ways of doing this with PIL & C, which there
undoubtedly are.
Cheers,
JB.
--
John Barratt
www.langarson.com.au - Python, Zope, GIS, Weather.
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |

|
2007-02-02 14:30:56 |
John Barratt wrote:
> Hi,
> I've had a need to do some optimisation of some low
level pixel setting
> code, and have as such done some tests of different
ways of doing this
> using PIL and alternatively with gd via ctypes.
Have you tried using PIL + numpy -- you can convert a PIL
image (some
eof them, anyway) to a numpy array, and manipulate it there.
Numpy
provides a number if efficient ways to do such
manipulations.
In the most recent PIL, you should be able to convert
to/from PIL/numpy
without any copying of the data, though I don't think it's
been heavily
tested.
-Chris
--
Christopher Barker, Ph.D.
Oceanographer
Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception
Chris.Barker noaa.gov
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |
  Australia |
2007-02-04 04:20:01 |
Christopher Barker wrote:
> John Barratt wrote:
>> I've had a need to do some optimisation of some
low level pixel setting
>> code, and have as such done some tests of different
ways of doing this
>> using PIL and alternatively with gd via ctypes.
>
> Have you tried using PIL + numpy -- you can convert a
PIL image (some
> eof them, anyway) to a numpy array, and manipulate it
there. Numpy
> provides a number if efficient ways to do such
manipulations.
>
> In the most recent PIL, you should be able to convert
to/from PIL/numpy
> without any copying of the data, though I don't think
it's been heavily
> tested.
I haven't tried it, but did originally think of adding it to
the list of
things tested. Given this specific example though, where
the idea is
that a specific method or piece of code is needed to be run
for every
pixel (not that it actually is in this abstract example), I
would think
that unless that specific code can be called from, and done
in C, that
it would perform similarly to the Image.load method. This
is because I
am assuming the cost of accessing the raw data from
Image.load to be
similar to that of accessing an array type in numpy. Having
said all
that though, I'll have a look at adding it to the mix...
I think where PIL & numpy would excel is where you have
a cases of a
number of images with calculations required that could be
easily
represented as array operations. The examples I am thinking
of don't
lend themselves to this sort of solution.
It is perhaps also possible to reasonably efficiently access
the PIL raw
data under C as a standard type (eg. a 'flattened' unsigned
char* for
paletted images) through ctypes, in which case the result
time-wise
would hopefully be similar, or the same as using gd all the
way.
I am also finishing a 'real world' example for this sort of
problem
which may help further clarify further options and their
relative
benefits & costs.
Cheers,
JB.
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |
  Australia |
2007-02-04 06:23:41 |
With prompting from Chris, and a little further work in gd,
I have
updated this comparison further, adding a further gd/ctypes
test, this
time setting pixel values using the raw pixel data, as well
as the start
of a test for a numpy/PIL option. Unfortunately it doesn't
fully work,
at least with the version/s I have. However it does, based
on what does
work, seem to be relatively slow. The new indicative times
look like this :
Method Time (s) Times slower than fastest
+-----+ +-------+ +------------------------+
ctypes,c,GD raw 0.00082 1.0
ctypes,c,GD 0.00177 2.2
PIL - 'load' 0.03226 39.4
ctypes, GD 0.14428 176.4
PIL, numpy * 0.26271 321.2
PIL - 'point' 0.37180 454.5
Further details are here, including updated code :
http://www.lan
garson.com.au/blog/?p=11
I also fixed a few bugs that affected the image created, but
not the
timing, and added code to save the files out where possible
to further
check they were actually doing what was intended.
Cheers,
JB.
--
John Barratt - www.langarson.com.au
Python, Zope, GIS, Weather
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |
  New Zealand |
2007-02-04 16:43:56 |
hi John,
> Method Time (s) Times slower than
fastest
> +-----+ +-------+
+------------------------+
> ctypes,c,GD raw 0.00082 1.0
> ctypes,c,GD 0.00177 2.2
> PIL - 'load' 0.03226 39.4
> ctypes, GD 0.14428 176.4
> PIL, numpy * 0.26271 321.2
> PIL - 'point' 0.37180 454.5
>
You can also access raw PIL image data via C, so your top
line,
"ctypes,c,GD raw" should be just as applicable to
PIL as GD. Or is
there some reason this is not so? From memory, you'd pry
open the
im.im object, and use something like im->image32[y][x] to
access each
pixel.
Also, your C functions boil down to variations of memset.
Have you
considered trying that? Another option is PIL's
paste(colour, box)
method. I suspect you are ignoring these two because
somewhere you
say "one pixel at a time", but as far as I can
tell your routines
don't allow for that pixel to vary, so there is actually no
difference. im.paste(colour, (0, 0, width, height)) is
functionally
the same as your test functions; inside it probably looks
much like
your setPixelsRaw.
douglas
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |
  Australia |
2007-02-04 17:22:18 |
Hi Douglas,
Douglas Bagnall wrote:
>> Method Time (s) Times slower than
fastest
>> +-----+ +-------+
+------------------------+
>> ctypes,c,GD raw 0.00082 1.0
>> ctypes,c,GD 0.00177 2.2
>> PIL - 'load' 0.03226 39.4
>> ctypes, GD 0.14428 176.4
>> PIL, numpy * 0.26271 321.2
>> PIL - 'point' 0.37180 454.5
>>
>
> You can also access raw PIL image data via C, so your
top line,
> "ctypes,c,GD raw" should be just as
applicable to PIL as GD. Or is
> there some reason this is not so? From memory, you'd
pry open the
> im.im object, and use something like
im->image32[y][x] to access each
> pixel.
Ahhh, that is the sort of thing I was wanting to do with
PIL, I just
didn't know how, though hadn't dug into the source code for
PIL either.
I will have a look to see if I can get this to fly as
well. I would
hope that in this case that things should be a similar/same
speed
between using PIL or gd as the main image object type. The
main reason
I did it with gd was because I could see a clear path from
the
documentation to achieve this optimisation.
> Also, your C functions boil down to variations of
memset. Have you
> considered trying that? Another option is PIL's
paste(colour, box)
> method. I suspect you are ignoring these two because
somewhere you
> say "one pixel at a time", but as far as I
can tell your routines
> don't allow for that pixel to vary, so there is
actually no
> difference. im.paste(colour, (0, 0, width, height)) is
functionally
> the same as your test functions; inside it probably
looks much like
> your setPixelsRaw.
The code that does the pixel setting in these tests is very
simple yes,
and deliberately so. It could be done other ways, this was
done as a
simple test case to get an idea of the overhead of the rest
of a system
like this. The idea is that each pixel requires some
relatively complex
piece of code to calculate each value, and one that can't be
obtained by
simple array/image operations that work on the whole image.
I have a
'real world' example that I will finish, and might help show
this more
clearly.
Cheers,
JB.
--
John Barratt - www.langarson.com.au
Python, Zope, GIS, Weather
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |
  Australia |
2007-02-05 04:22:09 |
Well after some digging through the PIL source code after
the prompting
from Douglas, and some tinkering with the python C
interface, I have
implemented a C based version of the core pixel setting loop
using PIL
objects. I have also added 'reference' versions using plain
python,
setting an integer value in the loop, and setting an integer
2D array in
a loop.
The short of this is that a PIL implementation with C goes
faster for
larger images (1024x1024), and faster almost overall if you
only
consider time to actually set the pixels, and not image
creation time as
well.
Fredrik, do you think it would be possible to have a
standard API call
in PIL that could cleanly & reliably expose the core C
pixel data array
for use within C (through say ctypes, or back through the
python-c api)
for optimisations like this?
The full breakdown of results, and new source code with the
PIL/c
implementation can be found here :
http://www.lan
garson.com.au/blog/?p=13
I think that has about exhausted the possible combinations
to test, but
if anyone has any other suggestions, please let me know.
Also if anyone
has any ideas as to why the PIL/raw/c version should go so
much faster
than the gd/raw/c version I would be interested to know, as
the core
looping code is basically identical...
Cheers,
JB.
--
John Barratt - www.langarson.com.au
Python, Zope, GIS, Weather
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |
  United States |
2007-02-05 12:28:13 |
John Barratt wrote:
> I haven't tried it, but did originally think of adding
it to the list of
> things tested. Given this specific example though,
where the idea is
> that a specific method or piece of code is needed to be
run for every
> pixel (not that it actually is in this abstract
example), I would think
> that unless that specific code can be called from, and
done in C, that
> it would perform similarly to the Image.load method.
This is because I
> am assuming the cost of accessing the raw data from
Image.load to be
> similar to that of accessing an array type in numpy.
Having said all
> that though, I'll have a look at adding it to the
mix...
Quite true. In fact, your tests make it look like accessing
individual
pixels in numpy is pretty darn slow. I'm guessing that
that's because
numpy may be creating a numpy scalar object with every pixel
access.
> I think where PIL & numpy would excel is where you
have a cases of a
> number of images with calculations required that could
be easily
> represented as array operations. The examples I am
thinking of don't
> lend themselves to this sort of solution.
Also true -- but are you sure you couldn't do your
pixel-specific
calculations as array operations?
By the way, you really want to be using numpy 1.0.1, not
numpy 0.9.8,
numpy was undergoing a lot of flux before 1.0 came out.
Also, it looks like your code wouldn't work anyway:
data = numpy.resize(numpy.array(0),outputSize)
for x in range(outputSize[0]):
for y in range(outputSize[1]):
data[x,y] = colourI
This creates a 2-d array of 32bit integers, which isn't
going to match
an RBG array. You'd need to either use RGBA, or use an WxHx3
array of bytes:
data = numpy.zeros(outputSize[0], outputSize[1], 3,
dtype=numpy.ubyte)
However, that would only make it slower! I am a bit
surprised that it is
as slow as that.
John Barratt wrote:
>> You can also access raw PIL image data via C, so
your top line,
>> "ctypes,c,GD raw" should be just as
applicable to PIL as GD. Or is
>> there some reason this is not so? From memory,
you'd pry open the
>> im.im object, and use something like
im->image32[y][x] to access each
>> pixel.
> Ahhh, that is the sort of thing I was wanting to do
with PIL, I just
> didn't know how,
There is a movement afoot (mostly us numpy folks) to get a
basic n-d
array object into the standard Python library. The goal is
that all
packages that need an nd-array of data (such as PIL, etc)
could use the
same thing, and then they'd all have the same C API for
accessing the
data -- this looks like another good argument for that!
> The idea is that each pixel requires some relatively
complex
> piece of code to calculate each value, and one that
can't be obtained by
> simple array/image operations that work on the whole
image.
careful here -- if your "relatively complex" piece
of code takes
substantially longer than the pixel setting, then all this
work is for
naught -- though we're all learning something from it!
by the way, numpy can do more than "simple array"
operations -- so it
still may help.
-Chris
--
Christopher Barker, Ph.D.
Oceanographer
Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception
Chris.Barker noaa.gov
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |
  New Zealand |
2007-02-05 17:59:48 |
John Barratt wrote:
> I think that has about exhausted the possible
combinations to test, but
> if anyone has any other suggestions, please let me
know. Also if anyone
> has any ideas as to why the PIL/raw/c version should go
so much faster
> than the gd/raw/c version I would be interested to
know, as the core
> looping code is basically identical...
>
um.. cache locality? With GD you have
for (u=0; u<x; u++) {
for (v=0; v<y; v++) {
d[v][u] = colour;
}
}
which goes through every row for each column, jumping back
and forth in
memory. You need to switch the order of the loops, like you
have in the
PIL example:
for (u=0; u<xSize; u++) {
for (v=0; v<xSize; v++) {
data[u][v] = colour;
}
}
...which, BTW, as written, would not be so successful on
non-square images.
However, this shows how far the benchmark has got from real
world usage.
As Chris Barker pointed out, if you are doing any kind of
work to find
your pixels, these effects would become negligible.
Have you looked at PyGame? it seems to have drawing commands
too.
Douglas Bagnall
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
| Re: Comparison of PIL and GD speed for
setting pixels through python |
  Australia |
2007-02-05 19:13:42 |
Hi Chris,
Christopher Barker wrote:
>> I think where PIL & numpy would excel is where
you have a cases of a
>> number of images with calculations required that
could be easily
>> represented as array operations. The examples I am
thinking of don't
>> lend themselves to this sort of solution.
>
> Also true -- but are you sure you couldn't do your
pixel-specific
> calculations as array operations?
Well, certainly not simple array operations, I have an
example I will
finish (hill shading) that perhaps could be done with array
operations,
but would I think still require a large number of them,
making it still
inefficient.
> By the way, you really want to be using numpy 1.0.1,
not numpy 0.9.8,
> numpy was undergoing a lot of flux before 1.0 came
out.
OK, I will update my version.
> Also, it looks like your code wouldn't work anyway:
>
> data = numpy.resize(numpy.array(0),outputSize)
> for x in range(outputSize[0]):
> for y in range(outputSize[1]):
> data[x,y] = colourI
>
> This creates a 2-d array of 32bit integers, which isn't
going to match
> an RBG array. You'd need to either use RGBA, or use an
WxHx3 array of
> bytes:
>
> data = numpy.zeros(outputSize[0], outputSize[1], 3,
dtype=numpy.ubyte)
>
> However, that would only make it slower! I am a bit
surprised that it is
> as slow as that.
OK, thanks. I was perhaps guessing at what was required
here, and since
I couldn't get the output to work I couldn't tell it was
actually
broken. I'll revisit this when I can get numpy working with
PIL,
perhaps the newer version will help.
> There is a movement afoot (mostly us numpy folks) to
get a basic n-d
> array object into the standard Python library. The goal
is that all
> packages that need an nd-array of data (such as PIL,
etc) could use the
> same thing, and then they'd all have the same C API for
accessing the
> data -- this looks like another good argument for
that!
mmm that would be nice. Would be nice to be able to combine
python with
the efficiency of C more easily for tasks like this!
>> The idea is that each pixel requires some
relatively complex piece of
>> code to calculate each value, and one that can't be
obtained by simple
>> array/image operations that work on the whole
image.
>
> careful here -- if your "relatively complex"
piece of code takes
> substantially longer than the pixel setting, then all
this work is for
> naught -- though we're all learning something from it!
Yes, true. It could be a relatively minor cost at the end of
the day if
the per-pixel operation is too costly. But there are
actually two
motivations here :
- If the per-pixel operation isn't too costly, then you get
a real
noticeable benefit of lower pixel-access overhead.
- Even if the per-pixel operation is relatively costly, with
this method
you can easily implement that per-pixel operation in C
because the core
pixel access loop is in C (with PIL or gd at the python
level). This in
turn should make it run significantly faster than it's pure
python
counterpart. This perhaps works only with the assumption
that it isn't
too much effort to write/rewrite that particular operation
in C.
> by the way, numpy can do more than "simple
array" operations -- so it
> still may help.
OK. It may indeed be able to do something more effectively
with the
next example.
Thanks for the comments,
JB.
--
John Barratt - www.langarson.com.au
Python, Zope, GIS, Weather
_______________________________________________
Image-SIG maillist - Image-SIG python.org
htt
p://mail.python.org/mailman/listinfo/image-sig
|
|
|
|