5

I need a python script which gets a dynamically created image file from given URL and create a material with that image file.

Then I will apply that material to my blender object.

The python code below works for local image files

import bpy, os

def run(origin):
    # Load image file from given path.
    realpath = os.path.expanduser('D:/color.png')
    try:
        img = bpy.data.images.load(realpath)
    except:
        raise NameError("Cannot load image %s" % realpath)

    # Create image texture from image
    cTex = bpy.data.textures.new('ColorTex', type = 'IMAGE')
    cTex.image = img

    # Create material
    mat = bpy.data.materials.new('TexMat')

    # Add texture slot for color texture
    mtex = mat.texture_slots.add()
    mtex.texture = cTex

    # Create new cube
    bpy.ops.mesh.primitive_cube_add(location=origin)

    # Add material to created cube
    ob = bpy.context.object
    me = ob.data
    me.materials.append(mat)

    return

run((0,0,0))

I tried :

import urllib, cStringIO

file = cStringIO.StringIO(urllib.urlopen(URL).read())
img = Image.open(file)

But I had no luck with it. First error I got was

ImportError: No module named 'StringIO'

Does python scripting API in Blender use restirictive Modules or what?

Thank you for your help.

enter image description here

K DawG
  • 13,287
  • 9
  • 35
  • 66
effe
  • 1,335
  • 3
  • 17
  • 26
  • Can you please fix the indentation. – RickyA Sep 29 '13 at 08:44
  • Are you on windows? -> yes you are. There python may be prepackaged with blender. What python version and what binary is used? – RickyA Sep 29 '13 at 08:48
  • I am using that python script in Blender 2.68a and in Blenders console it says PYTHON INTERACTIVE CONSOLE 3.3.0 (default, Nov 26 2012, 17:23:29) [MSC v.1500 32 bit (Intel)], but i dont know python version of Blender. – effe Sep 29 '13 at 08:55
  • Can you do a 'import sys' and a 'print(sys.version_info)' from that script and run it like you would do with the normal script? – RickyA Sep 29 '13 at 09:22

3 Answers3

3

You seem to use Python 3.3, which doesn't have cStringIO. Use io.BytesIO instead:

import io
data = io.BytesIO(urllib.urlopen(URL).read())

[EDIT]

tested in Blender 2.68a on osx:

import io
from urllib import request
data = io.BytesIO(request.urlopen("http://careers.stackoverflow.com/jobs?a=288").read())
data
>>>> <_io.BytesIO object at 0x11050aae0>

[EDIT2]

Ok, is seems blender can only load from a file. Here is a modification of your script that downloads an url, stores it in a temp location, creates the material from that, packs the material in the blend file and deletes the temp image.

import bpy, os, io from urllib import request

def run(origin):
    # Load image file from url.    
    try:
        #make a temp filename that is valid on your machine
        tmp_filename = "/tmp/temp.png"
        #fetch the image in this file
        request.urlretrieve("https://www.google.com/images/srpr/logo4w.png", tmp_filename)
        #create a blender datablock of it
        img = bpy.data.images.load(tmp_filename)
        #pack the image in the blender file so...
        img.pack()
        #...we can delete the temp image
        os.remove(tmp_filename)
    except Exception as e:
        raise NameError("Cannot load image: {0}".format(e))
    # Create image texture from image
    cTex = bpy.data.textures.new('ColorTex', type='IMAGE')
    cTex.image = img
    # Create material
    mat = bpy.data.materials.new('TexMat')
    # Add texture slot for color texture
    mtex = mat.texture_slots.add()
    mtex.texture = cTex
    # Create new cube
    bpy.ops.mesh.primitive_cube_add(location=origin)
    # Add material to created cube
    ob = bpy.context.object
    me = ob.data
    me.materials.append(mat)

run((0,0,0))

Output: enter image description here

RickyA
  • 15,465
  • 5
  • 71
  • 95
  • No module named 'io.BytesIO'; io is not a package – effe Sep 29 '13 at 09:16
  • mmm, weird. can you do a 'print(dir(io))' and 'print(dir(io.BytesIO))' in that script and run it. – RickyA Sep 29 '13 at 09:21
  • >>> print(dir(urllib)) ['__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', '__loader__', '__name__', '__package__', '__path__'] – effe Sep 29 '13 at 09:23
  • >>> print(dir(io)) Traceback (most recent call last): File "", line 1, in NameError: name 'io' is not defined – effe Sep 29 '13 at 09:23
  • I added a screenshot of Blender console at the end of question. – effe Sep 29 '13 at 09:28
  • did you do a 'import io'? it is working fine with me (2.68a, osx). – RickyA Sep 29 '13 at 09:32
  • >>> import IO Traceback (most recent call last): File "", line 1, in ImportError: No module named 'IO' – effe Sep 29 '13 at 09:37
  • URL = 'https://www.google.com/images/srpr/logo6w.png' data = io.BytesIO(urllib.urlopen(URL).read()) img = Image.open(data) – effe Sep 29 '13 at 09:49
  • Can you test the edit in the answer. urllib also has been revamped. – RickyA Sep 29 '13 at 09:51
  • data = io.BytesIO(request.urlopen("https://www.google.com/images/srpr/logo6w.png").read()) // worked but now how will use this data as image, I tried img = Image.open(data) but it did not work – effe Sep 29 '13 at 09:59
  • MMm blender seems not to be able to load a stream object as texture. See edit two for a workaround. Do update your temp file locatation since you are on windows. – RickyA Sep 29 '13 at 10:28
  • It is OK if there is no way to create materail if it is not in file system. Thank you so much for your efforts. I tried your script and it works now. I will go on from that point. – effe Sep 29 '13 at 11:33
1

Simply use urllib.urlretrieve(url, localfilename) then use the local file.

Steve Barnes
  • 27,618
  • 6
  • 63
  • 73
  • Is there any way of doing it without retreiving because this task will be dynamic and repetitive. I dont want to keep that temporary image files. – effe Sep 29 '13 at 09:02
0

You can copy the pixel data, but I'm not sure if it's worth the trouble. Probably it doesn't work with all file formats.

import bpy, io, requests, PIL

def loadImageFromUrl(url,name=None):
    frames = ()
    # load image
    image = PIL.Image.open(io.BytesIO(requests.get(url).content))
    try:
        while True:
            # create new blender image of correct size
            frame = bpy.data.images.new(name or url, image.width, image.height)
            # copy the pixel data. apparently the lines have to be flipped.
            frame.pixels = [
                value / 255
                for pixel in image.transpose(PIL.Image.FLIP_TOP_BOTTOM)
                                  .convert('RGBA')
                                  .getdata()
                for value in pixel
            ]
            frames += frame,
            image.seek(len(frames))
    except EOFError:
        return frames
Robert
  • 2,603
  • 26
  • 25