Image Python API for Blender

It’s come up occasionally that Blender’s low-level image access is quite limited (only access to users image data-blocks, very limited image manipulation).

Recently a developer requested we bundle pillow Python module with Blender,
after some discussion we would prefer to improve and expose Blender’s existing image API (ImBuf).

I’ve outlined reasons in this design task

Since this isn’t a priority for 2.8 - maybe this is interesting for someone getting involved in development.

8 Likes

Well to be fair Pillow will indeed increase Blender size considerably but it does come with a lot of functionality.

On the other hand Blender can also do a lot with images so an Image python API makes even more sense.

I am actually using PIL to load images for BGL (to avoid the clumsy standard approach of loading them via the image editor using the existing Python API) and having this done natively by Blender sounds like a much better idea. I will take a look at the code and see if I can expose at least the loading part.

There was a crude implementation of PIL in blender at the moment(2.79). For example, you can find an image in the console, and do this:

Image.scale(1024, 1024)

It will resize the image to 1024x1024 pixels in size.

1 Like

A while back I started a patch for an image API in Blender, only basic operations are supported at the moment.

Update: committed the patch, while it’s only basic operations this makes it easier to accept improvements to the API.

Example use:

import imbuf

imb = imbuf.load("test.png")
imb.resize((30, 30))
imb.ppm = 33.0, 22.5
imbuf.write(imb, "test_out.png")
6 Likes

Either you are too fast, or I am too slow, in any case, well done !!!

1 Like

@kilon no matter - the API is very primitive - so there are many possible additions.

I assumed because you closed the task that you did not want for it to go further. I have to confess I am a bit confused about the whole Blender developer contribution process because I am new to it. But I am very interested into starting my own fork , I am to focus more on UI stuff but the image python api was one of my ideas so I dont have to use PIL and I can natively use Blender internal code. It would be better if things that are agreeable / acceptable from blender bf devs are contributed to blender source than to my own fork.

I was developing my own add on because I wanted to avoid modifying Blender source and maintaining my own fork but it became apparent that I was getting tired of the limitation of the Blender python api. Very powerful and well designed but not for what I wanted to do.

I am now in the process of decipher the blender source code (2.8 ui stuff mainly), I will take also a look at the image functions to see if I can make some small patches for contribution that will expose those c functions to python because I am definetly interested in extending the Blender Python API.

I will use this as a thread for my questions and ideas if you dont mind.

I’ve re-opened T54272 (corrected link) - since most operations here aren’t included in the initial commit.

Suggestions for basic patches for anyone wanting to get started:

  • copy method: (IMB_dupImBuf)
  • load image from memory (IMB_ibImageFromMemory).
  • load image from file handle (IMB_loadifffile).
  • create image from pixel buffer (IMB_allocFromBuffer), using bytes/bytearray - using Python’s buffer API.

We could also have byte/float operations added (convert between float/byte buffers), suggest to get byte access working well first.

Some image processing that I’ve done in Blender in the past has involved joining several images together, if that makes sense (imagine rendering an image in several ‘tiles’ and then wanting to join those tiles together into a single image).

This was quite difficult and I ended up just writing my own methods as I don’t think Blender’s pixels were in the right configuration for some of the image processing libraries (or maybe it was just my unfamiliarity with them). My methods were also quite slow. I’m not sure if imbuf would be ‘fast’ per se, but it would be definitely convenient.

One other thing would be some basic ‘mix/overlay’ functions, like being able to overlay an image with alpha over the top of another image. My example for when I’ve needed this is when rendering several images with different render borders and wanting to combine them. (It could be that being able to overlay images actually solves the need to ‘join’ images in my previous example.)

I’ve re-opened T55162 - since most operations here aren’t included in the initial commit.

@ideasman42 Did you reference the right development task here? Did you mean T54272?

Copying between images will be fast.

To begin with we could have copying (also called blitting) functions - see: IMB_BLEND_COPY use in IMB_rectblend. Id rather avoid supporting all blending options up front.

Ok I will start working on it on Monday. Starting with your suggestions. Will keep you posted.

@ideasman42 Is it ok if I work directly on Blender 2.8 code ?

I see you frequently merge master to 2.8 so getting the latest commits should not be an issue. Is there a reason to prefer the 2.79 [master] branch instead of the [blender 2.8] branch ?

[master] is delightfully stable compared to [blender 2.8], sure it doesn’t have all the shiny new features, but on the upside: it doesn’t have the instability of all those shiny new features either…

1 Like

@LazyDodo I am not afraid of the crashes, its part of the game anyway, but you make a valid point if I may not be able to use img_buf. I will give 2.8 a try and if all fails I will drop down to master. But if its a few crashes , I will stick with 2.8 cause I like to take advantage of the new code and it’s new features and I want to understand the new code base for my own fork which is why I decided to have a bite on this API as a warm up.

Ok I hope I ask this in the right place, after a day with battling with reducing my build times from 30 minutes to a minute, I managed to reduce it down to 10 seconds. Which makes coding bearable.

So first question I see the methods are formated like this example


static PyObject *M_imbuf_new(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
{
	int size[2];
-->	static const char *_keywords[] = {"size", NULL};
	static _PyArg_Parser _parser = {"(ii)|i:new", _keywords, 0};
	if (!_PyArg_ParseTupleAndKeywordsFast(
	        args, kw, &_parser,
	        &size[0], &size[1]))
	{
		return NULL;
	}

	/* TODO, make options */
	uchar planes = 4;
	uint flags = IB_rect;

	ImBuf *ibuf = IMB_allocImBuf(UNPACK2(size), planes, flags);
	if (ibuf == NULL) {
		PyErr_Format(PyExc_ValueError, "new: Unable to create image (%d, %d)", UNPACK2(size));
		return NULL;
	}
	return Py_ImBuf_CreatePyObject(ibuf);
}

4th line, why the pointer variable is named _keywords[] instead of _args[] ?

Please note I am new to this Python C API dance, so I reading documentation while trying to understand Blender code using VS debugger. Things are starting to make sense but still I am in an infant mode.

Care to elaborate on this? Why did a build take 30 minutes for you? sure initial build will be a bit, but beyond that it should be both incremental compiles and links.

Probably I was doing something wrong and was rebuilding the whole thing.

In any case I am now using the lite release and 10 seconds are good enough for now and see in the future if I could reduce it down to 1. But right now my priority is to learn the Blender code and be useful. So that 1 second build time will have to wait.

I have developed a small library for C++ for live coding, which essentially build a super minimal executable and keep everything in DLLs so it keeps compile times in under a second and it does rebuids in the background while code is saved, it also keeps memory intact which is also handled by the DLLs. It was not my idea, I was inspired by another developer that publishes the handmade hero tutorials on youtube. His goal was to use C++ as a scripting language instead of python, like Blender does with its addons. Unreal uses also a similar technique for reloading/rebuilding C++ code, real time, while in its editor.

So build times is a temporary problem for now.

Of course Blender is far more monolithic so I will have to adjust that library, but as I said thats not what my priority is right now and for now I only care to understand the basics of the code , hence my question.

Allright them back to your question, it’s called keywords cause

  1. it contains the keywords
  2. it goes into the keywords field of the _PyArg_Parser structure.

but in all fairness, it’s just a variable name, could have been named LazyDodoLikesKittens and it would have still worked, however nicely chosen names do make the code more readable.

I’m not sure what the arguments could be for calling it _args.

1 Like

Well my logic was that since it refers to python arguments and not python keywords , I would expect it to be named _args but now I see as I suspected the rabbit hole goes deeper.

Yeah I am rather strict how I name things, but my motivation for this question was to understand the intention. To better understand how Blende deals with such functionality. I thought there was more it, glad to see the explanation is simple enough.