Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support numpy's __array_interface__ #65

Closed
joeshaw opened this issue Oct 9, 2012 · 15 comments
Closed

Support numpy's __array_interface__ #65

joeshaw opened this issue Oct 9, 2012 · 15 comments
Labels
Milestone

Comments

@joeshaw
Copy link

joeshaw commented Oct 9, 2012

It would be nice if you could provide a wand image to numpy.asarray() and have it automatically create a properly sized and shaped numpy matrix.

Right now if you try to pass an image in, things bog down and 100% CPU is used. I suspect this is due to the iteration interface on wand.image.Image creating a Color object for every pixel.

To support this, Wand would need to have a __array_interface__ property which returned some information about the data. You can find some information on the interface at http://scipy-lectures.github.com/advanced/advanced_numpy/index.html#array-interface-protocol

Basically, I think something like this would work, at least for raw RGB:

with Image.open(filename="/tmp/some.jpg") as img:
    array = numpy.asarray(img)

And the __array_interface__ would be something like:

@property
def __array_interface__(self):
    return dict(data=self.make_blob("RGB"),
                shape=(self.width, self.height, 3),
                typestr="|u1")

You might want to use the image's internal format so you could support things like RGBA; I'm not sure what the right thing to do here is. (And in that case, shape would be 4 for the third argument.)

@dahlia
Copy link
Collaborator

dahlia commented Oct 10, 2012

That’s a good idea. I will look into this soon.

@joeshaw
Copy link
Author

joeshaw commented Oct 10, 2012

Oh, also it would be nice if the Image constructor took an array argument to convert it back.

Thanks for looking into this!

@dahlia
Copy link
Collaborator

dahlia commented Oct 19, 2012

I just started to work on this. See numpy branch.

dahlia added a commit that referenced this issue Mar 22, 2013
- Deferred Python 3 compatibility to 0.4 (from 0.3).
- Add ImageMagick CLI to Wand compiler (#100).
- Add supporting __array_interface__ of NumPy (#65).
dahlia added a commit that referenced this issue Apr 22, 2013
@emcconville emcconville added this to the 0.5.0 milestone Nov 30, 2018
@emcconville
Copy link
Owner

Although __array_interface__ was implemented, I never got to an array constructor.

@emcconville emcconville removed this from the 0.5.0 milestone Jan 2, 2019
@fmw42
Copy link

fmw42 commented Mar 30, 2019

Hello

I am a novice Python user, but know ImageMagick well.

I have been working on a Python script where it would be useful to import numpy arrays (from Scipy processing) into Wand images, so I can use them to do some Wand composite operations.

Are you still working on a method to import numpy arrays into wand images? If that is still in the works, can you suggest some other way to convert numpy arrays, say, to strings and then from strings to blob and then import the blob into a Wand image?

@emcconville
Copy link
Owner

Hey Fred,

Hoping to return to this effort soon. A quick work-around is to use Image.import_pixels method, over a blank canvas.

w, h, p = array.shape

with Image(width=w, height=h) as img:
    img.import_pixels(width=w,
                      height=h,
                      channel_map="RGBA",
                      storage="char",
                      data=list(array.flat))

You'll have to do handle the storage & channel mapping.

@fmw42
Copy link

fmw42 commented Mar 31, 2019 via email

@emcconville
Copy link
Owner

Thanks for the kind words! Feel free to email me directly with additional questions about your project (address provided on profile page).

Will that work with floating point arrays

Should, as long as the float points are normalized between 0.0 and 1.0. This question & answer seems to cover that w/ numpy.

As far as the import_pixels method supporting floats, you just need to ensure that storage kwargs is set to "float".


I'll probably have a proposed solution for a "from array" constructor in the next day if folks are willing to help test / provided test-cases.

@fmw42
Copy link

fmw42 commented Mar 31, 2019 via email

@fmw42
Copy link

fmw42 commented Mar 31, 2019

Eric,

I tested your method from above of importing a numpy array into Wand. I created a red numpy 3 channel array and imported it and then saved it to file. But it came out transparent red.

But I finally solved it (after finding your note about alpha in the section on import_pixels) by adding rimg.alpha_channel = 'off'. So ignore my subsequent messages.

#!/bin/python3.7

import numpy as np
from wand.image import Image
from wand.display import display

# create numpy red image array for background
red = np.zeros([100, 100, 3], dtype=np.uint8)
red[:,:] = [255, 0, 0]

# load red array into Wand and save to PNG file red.png
w, h, c = red.shape
with Image(width=w, height=h) as rimg:
    rimg.import_pixels(width=w,
                      height=h,
                      channel_map="RGB",
                      storage="char",
                      data=list(red.flat))
    rimg.format = 'png'
    rimg.alpha_channel = 'off'
    rimg.save(filename='red.png')
    display(rimg)

Also what do I need to add to display the red image? My attempts with display(rimg) have failed.

I finally solved the display issue, also. Not sure why it did not work before, but it is working now.

Thanks in advance for your help.

P.S. I would be willing to test your "from array" constructor with this case and my project case as well.

@fmw42
Copy link

fmw42 commented Mar 31, 2019

I removed most of my messages after solving the transparency issue. Sorry for being such a novice.

@emcconville
Copy link
Owner

@fmw42,

Happy you have made progress with the work-around. Sorry I wasn't able to respond sooner.

For the transparency issue, this is IM-7 behavior change. Setting the default background will fix it (on IM-7 the 'xc:none' adds an empty alpha channel).

from wand.color import Color
# ...
with Image(width=w, height=h, background=Color('WHITE')) as rimg:
    # ...

For the display issue on OS X, setting the Image.format is all that's needed. Omitting such will have OS attempting to open a .miff file, and not understanding what to do.


For the "from array" implementation, I've created a class method Image.from_array, and pushed a work-in-progress branch entitled issue_65. I'll be attempting to do some additional testing, and feedback is very much welcome. An example usage would be something like ...

import numpy
from wand.image import Image

sample = numpy.random.rand(100, 100, 3)

with Image.from_array(sample) as img:
    img.save(filename='output.png')

Optional kwargs of channel_map & storage has been provided for arrays that Wand can't figure out / map.

@fmw42
Copy link

fmw42 commented Apr 1, 2019 via email

@emcconville emcconville added this to the Wand 0.5.3 milestone Apr 2, 2019
emcconville added a commit that referenced this issue Apr 4, 2019
@emcconville
Copy link
Owner

Support for numpy array's has been merged into master branch. Folks interested in testing, you can create an image from an array by using Image.from_array.

import numpy as np
from wand.image import Image

array = np.random.rand(100, 100, 3)
with Image.from_array(array) as img:
    img.save(filename='output.png')

output

@fdoumet
Copy link

fdoumet commented Feb 12, 2020

Using wand 0.5.9, and getting the following error:
AttributeError: 'module' object has no attribute 'from_array'
even though release notes claim this was released in 0.5.3.
Please re-open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants