treeform / pixie

Full-featured 2d graphics library for Nim.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Raw file format support

amaank404 opened this issue · comments

commented

Now I do not mean that pixie should support the camera type raw photo. I mean actual pixel data stored in a file with given format.
For example, given a function image.saveraw("file.raw", pRGBA). where pRGBA is a enum value for pixelFormats. all the channels shall be 8-bit for the RGBA and that is 4bytes per pixel. To convert from RGBX to RGBA, a converter would be used to unmultiply/divide using the alpha value. Then the pixel data would be scaled in the range of a uint8 and that is 0 to 255. And then finally without any padding or resolution information, the pixel data will be stored in a string object.

Example input data (hex)

  0        1        2
0 ffffff01 ff00ff02 00ffff03
1 abffff04 ff06ff05 08ffff06
2 ffabcd09 12ff2208 89ffcc07

Each value above is a hex value representing R, G, B, A components in order. and the above image is 3x3 in size (obviously, it would be much more realistic size in real world). Raw data generated for the above in hex would be

ffffff01ff00ff0200ffff03abffff04ff06ff0508ffff06ffabcd0912ff220889ffcc07

Now, you might ask, what are the real practical usages of the above. Well, to interface with another library. In my case, I shall need it in the pixie-python package to convert the pixie generated images to pygame surfaces and then those pygame surfaces to numpy array that would be used by moviepy to create animations and videos.

if you're using pixie-python, you can access the raw bytes without copying using the getImageRawData function below:

from ctypes import Structure, POINTER, byref, sizeof, c_ubyte, c_longlong, cast
from pixie import pixie

class ImageData(Structure):
	_fields_ = [
		("length", c_longlong),
		# ("data", c_ubyte * self.length.value * 4)
	]

class ImageRef(Structure):
	_fields_ = [
		("width", c_longlong),
		("height", c_longlong),
		("length", c_longlong),
		("data_ptr", POINTER(ImageData)),
	]

def getImageRawData(self: pixie.Image):
	img_ref = cast(self.ref, POINTER(ImageRef)).contents
	data_length = img_ref.data_ptr[0].length
	data_type = c_ubyte * (data_length * 4)
	data = cast(byref(img_ref.data_ptr[0], sizeof(img_ref.data_ptr[0])), POINTER(data_type)).contents
	return data

the returned data will be a little inconvenient to use, since it's a ctype array. so you might want to use bytearray function to get a bytearray string of the data. however, this will involve a small copying overhead.

# img = pixie.Image(64, 32); # then paint the img
img_bytes = bytearray(getImageRawData(img)

also, if you're ultimately going to feed the bytearray to either PIL or numpy, it'll probably be better to use the functions below:

from PIL import Image as PILImage
import numpy as np

def getPILImage(self: pixie.Image):
	return PILImage.frombuffer("RGBA", (self.height, self.width), getImageRawData(self), "raw")

def getNPArray(self: pixie.Image):
	return np.frombuffer(getImageRawData(self), dtype=np.uint8).reshape((self.height, self.width, 4))

# optional monkey-patching:

pixie.Image.getImageRawData = getImageRawData # `img.getImageRawData()` gives raw ctype bytes
pixie.Image.getPILImage = getPILImage # `img.getPILImage()` gives a `PIL.Image` view of the image without copying
pixie.Image.getNPArray = getNPArray # `img.getNPArray()` gives `np.ndarray`

pixie.Image.data = property(getImageRawData) # `img.data` gives raw ctype bytes
pixie.Image.pil = property(getPILImage) # `img.pil` gives a `PIL.Image` view of the image without copying
pixie.Image.array = property(getNPArray) # `img.array` gives `np.ndarray`
commented

Thank you, now I have long given up on this one so I wouldn't be using it anymore but might help other helpless wanderers like myself.