refactor FloatImg class method to function imported in floatimg module. add tests for file dump / load : coverage of exixting python code is 100%, yeah

This commit is contained in:
John Doe 2021-05-12 08:28:06 +02:00
parent f5200d6fba
commit 1d4e2242cd
6 changed files with 212 additions and 66 deletions

View File

@ -15,7 +15,7 @@ The file `floatimg/settings.py` contains the path to the .so file.
The FloatImg class encapsulate core and file functionnalities of FloatImg library. Import if from the main module: The FloatImg class encapsulate core and file functionnalities of FloatImg library. Import if from the main module:
```python ```python
from floatimg import FloatImg import floatimg
``` ```
### Image creation ### Image creation
@ -23,12 +23,12 @@ from floatimg import FloatImg
`FloatImg` has two class methods to create an image. `FloatImg` has two class methods to create an image.
```python ```python
img = FloatImg.create(640, 480, FloatImg.RGB) img = floatimg.create(640, 480, floatimg.RGB)
``` ```
Or use the `create_rgb` shortcut: Or use the `create_rgb` shortcut:
```python ```python
img = FloatImg.create_rgb(640, 480) img = floatimg.create_rgb(640, 480)
``` ```
Note that calling the `destroy` method of the instance is not necessary as it will be called automatically at instance destruction. Note that calling the `destroy` method of the instance is not necessary as it will be called automatically at instance destruction.
@ -36,7 +36,11 @@ Note that calling the `destroy` method of the instance is not necessary as it wi
Or completely duplicate an existing image: Or completely duplicate an existing image:
```python ```python
# without copying data
new_img = img.clone() new_img = img.clone()
# with copying data
new_img = img.clone(1)
``` ```
### Image manipulation ### Image manipulation
@ -82,12 +86,12 @@ img.load_from_dump("/tmp/image_dump")
#### Create a new image from a dump file #### Create a new image from a dump file
```python ```python
img = FloatImg.create_from_dump("/tmp/image_dump") img = floatimg.create_from_dump("/tmp/image_dump")
``` ```
#### Get dump metadata #### Get dump metadata
```python ```python
witdh, height, img_type = FloatImg.fileinfos("/tmp/image_dump") witdh, height, img_type = floatimg.fileinfos("/tmp/image_dump")
``` ```

View File

@ -1,5 +1,6 @@
"""Python Binding to FloatImg """
# FloatImg class and static functions
from floatimg.image import FloatImg from floatimg.image import FloatImg, create, create_rgb, create_from_dump, GRAY, RGB, fileinfos
__version__ = "0.0.1" __version__ = "0.0.1"

View File

@ -2,8 +2,18 @@ import ctypes as ct
from floatimg.settings import LIB from floatimg.settings import LIB
############################################################################################################
# type constants
GRAY = 1
RGB = 3
RGBA = 4
RGBZ = 99
############################################################################################################
# Type mapping
class C_FloatImg(ct.Structure): class C_FloatImg(ct.Structure):
"""mapping to the C structure of FloatImg""" """mapping to the C structure of FloatImg"""
pass pass
@ -66,16 +76,31 @@ c_fimg_plot_rgb.restype = ct.c_int
# int fimg_get_rgb(FloatImg *head, int x, int y, float *rgb); # int fimg_get_rgb(FloatImg *head, int x, int y, float *rgb);
c_fimg_get_rgb = LIB.fimg_get_rgb c_fimg_get_rgb = LIB.fimg_get_rgb
c_fimg_get_rgb.argtypes = (ct.POINTER(C_FloatImg), ct.c_int, ct.c_int, ct.POINTER(ct.c_float * 3)) c_fimg_get_rgb.argtypes = (
ct.POINTER(C_FloatImg),
ct.c_int,
ct.c_int,
ct.POINTER(ct.c_float * 3),
)
c_fimg_get_rgb.restype = ct.c_int c_fimg_get_rgb.restype = ct.c_int
# int fimg_put_rgb(FloatImg *head, int x, int y, float *rgb); # int fimg_put_rgb(FloatImg *head, int x, int y, float *rgb);
c_fimg_put_rgb = LIB.fimg_put_rgb c_fimg_put_rgb = LIB.fimg_put_rgb
c_fimg_put_rgb.argtypes = (ct.POINTER(C_FloatImg), ct.c_int, ct.c_int, ct.POINTER(ct.c_float * 3)) c_fimg_put_rgb.argtypes = (
ct.POINTER(C_FloatImg),
ct.c_int,
ct.c_int,
ct.POINTER(ct.c_float * 3),
)
c_fimg_put_rgb.restype = ct.c_int c_fimg_put_rgb.restype = ct.c_int
c_fimg_rgb_constant = LIB.fimg_rgb_constant c_fimg_rgb_constant = LIB.fimg_rgb_constant
c_fimg_rgb_constant.argtypes = (ct.POINTER(C_FloatImg), ct.c_float, ct.c_float, ct.c_float) c_fimg_rgb_constant.argtypes = (
ct.POINTER(C_FloatImg),
ct.c_float,
ct.c_float,
ct.c_float,
)
c_fimg_rgb_constant.restype = ct.c_int c_fimg_rgb_constant.restype = ct.c_int
c_fimg_dump_to_file = LIB.fimg_dump_to_file c_fimg_dump_to_file = LIB.fimg_dump_to_file
@ -100,12 +125,6 @@ class FloatImg:
:attr c_img: an pointer to the c_img structure :attr c_img: an pointer to the c_img structure
""" """
# type constants
GRAY = 1
RGB = 3
RGBA = 4
RGBZ = 99
# proxy attributes to the C_FloatImg structure # proxy attributes to the C_FloatImg structure
magic = property(lambda self: self.c_img.magic) magic = property(lambda self: self.c_img.magic)
width = property(lambda self: self.c_img.width) width = property(lambda self: self.c_img.width)
@ -129,21 +148,6 @@ class FloatImg:
def __str__(self): def __str__(self):
return f"<{self.__class__.__name__} instance {id(self)} width={self.width} height={self.height} type={self.str_type}>" return f"<{self.__class__.__name__} instance {id(self)} width={self.width} height={self.height} type={self.str_type}>"
#######################################################################################################
@classmethod
def create(cls, witdh, height, type_id):
"""create an new FloatImg instance"""
assert cls.type_is_valid(type_id)
img = C_FloatImg()
assert c_fimgcreate(ct.pointer(img), witdh, height, type_id) == 0
return FloatImg(img)
#######################################################################################################
@classmethod
def create_rgb(cls, witdh, height):
"""create a new rgb instance """
return cls.create(witdh, height, cls.RGB)
####################################################################################################### #######################################################################################################
def destroy(self): def destroy(self):
"""destroy the underlying structure. automattically called at instance destruction""" """destroy the underlying structure. automattically called at instance destruction"""
@ -169,12 +173,6 @@ class FloatImg:
def copy_data(self, to_img): def copy_data(self, to_img):
assert c_fimg_copy_data(self.c_img_p, to_img.c_img_p) == 0 assert c_fimg_copy_data(self.c_img_p, to_img.c_img_p) == 0
#######################################################################################################
@staticmethod
def type_is_valid(type_id):
"""return True if type_id is a valid one"""
return c_fimg_type_is_valid(type_id) == 1
####################################################################################################### #######################################################################################################
@property @property
def str_type(self): def str_type(self):
@ -216,13 +214,15 @@ class FloatImg:
def load_from_dump(self, fname): def load_from_dump(self, fname):
"""load data from a dump. size and type have to be compatible""" """load data from a dump. size and type have to be compatible"""
assert ( assert (
c_fimg_load_from_dump(ct.c_char_p(bytes(fname, encoding="utf8")), self.c_img_p) c_fimg_load_from_dump(
ct.c_char_p(bytes(fname, encoding="utf8")), self.c_img_p
)
== 0 == 0
) )
#######################################################################################################
@classmethod ###########################################################################################################
def fileinfos(cls, fname): def fileinfos(fname):
"""return witdh, height, img_type triplet read from a dump file""" """return witdh, height, img_type triplet read from a dump file"""
datas = (ct.c_int * 3)() datas = (ct.c_int * 3)()
assert ( assert (
@ -231,11 +231,32 @@ class FloatImg:
) )
return datas[:3] return datas[:3]
#######################################################################################################
@classmethod ###########################################################################################################
def create_from_dump(cls, fname): def create(witdh, height, type_id):
"""create an new FloatImg instance"""
assert type_is_valid(type_id)
img = C_FloatImg()
assert c_fimgcreate(ct.pointer(img), witdh, height, type_id) == 0
return FloatImg(img)
###########################################################################################################
def create_rgb(witdh, height):
"""create a new rgb instance """
return create(witdh, height, RGB)
###########################################################################################################
def create_from_dump(fname):
"""Create a new instance from a dump file""" """Create a new instance from a dump file"""
witdh, height, img_type = cls.fileinfos(fname) witdh, height, img_type = fileinfos(fname)
img = cls.create(witdh, height, img_type) img = create(witdh, height, img_type)
img.load_from_dump(fname) img.load_from_dump(fname)
return img return img
###########################################################################################################
def type_is_valid(type_id):
"""return True if type_id is a valid one"""
return c_fimg_type_is_valid(type_id) == 1

View File

@ -1,3 +1,5 @@
import ctypes import ctypes
LIB = ctypes.cdll.LoadLibrary("../FloatImg/libfloatimg.so") # LIB = ctypes.cdll.LoadLibrary("../FloatImg/libfloatimg.so")
LIB = ctypes.cdll.LoadLibrary("../FloatImg4PythonBinding/build/lib/libfloatimg.so")

View File

@ -1,37 +1,105 @@
from floatimg import FloatImg """test of core functions """
import floatimg
def test_create(): def test_create():
width = 640 width = 640
height = 480 height = 480
img = FloatImg.create(width, height, FloatImg.RGB) img = floatimg.create(width, height, floatimg.RGB)
assert img.width == width assert img.width == width
assert img.height == height assert img.height == height
assert img.type_id == FloatImg.RGB assert img.type_id == floatimg.RGB
img = FloatImg.create_rgb(width, height) img = floatimg.create_rgb(width, height)
assert img.type_id == FloatImg.RGB assert img.type_id == floatimg.RGB
# TODO inspect RVB # TODO inspect RVB
def test_clone(): def test_clone():
width = 640 width = 5
height = 480 height = 5
img = FloatImg.create(width, height, FloatImg.RGB) img = floatimg.create(width, height, floatimg.RGB)
img.rgb_constant(127.0, 127.0, 127.0)
# clone without copying pixel values
img2 = img.clone() img2 = img.clone()
assert img.width == img2.width assert img.width == img2.width
assert img.height == img2.height assert img.height == img2.height
assert img.type_id == img2.type_id assert img.type_id == img2.type_id
# TODO inspect RVB and do pixel per pixel comparison # TODO inspect RVB and do pixel per pixel comparison
for y in range(height):
for x in range(width):
assert img.get_rgb(x, y) != img2.get_rgb(x, y)
img2 = img.clone(1)
assert img.width == img2.width
assert img.height == img2.height
assert img.type_id == img2.type_id
# TODO inspect RVB and do pixel per pixel comparison
for y in range(height):
for x in range(width):
assert img.get_rgb(x, y) == img2.get_rgb(x, y)
def test_rgb_constant(): def test_rgb_constant():
width = 5 width = 5
height = 5 height = 5
color = [127.0, 127.0, 127.0] color = [127.0, 127.0, 127.0]
img = FloatImg.create_rgb(width, height) img = floatimg.create_rgb(width, height)
img.rgb_constant(*color) img.rgb_constant(*color)
for y in range(height): for y in range(height):
for x in range(width): for x in range(width):
assert img.get_rgb(x, y) == color assert img.get_rgb(x, y) == color
def test_copy_data():
width = 5
height = 5
color = [127.0, 127.0, 127.0]
img = floatimg.create_rgb(width, height)
img.rgb_constant(*color)
img2 = floatimg.create_rgb(width, height)
img2.rgb_constant(64.0, 64.0, 64.0)
img.copy_data(img2)
for y in range(height):
for x in range(width):
assert img.get_rgb(x, y) == img2.get_rgb(x, y)
def test_clear():
width, height = 5, 5
color = [127.0, 127.0, 127.0]
img = floatimg.create_rgb(width, height)
img.rgb_constant(*color)
img.clear()
for y in range(height):
for x in range(width):
assert img.get_rgb(x, y) == [0.0, 0.0, 0.0]
def test_str_type():
width, height = 5, 5
img = floatimg.create_rgb(width, height)
assert img.str_type == "rgb"
def test__str__():
width, height = 5, 5
img = floatimg.create_rgb(width, height)
img_str = str(img)
assert " width=5 " in img_str
assert " height=5 " in img_str
assert " type=rgb>" in img_str
def test_put_rgb():
width, height = 5, 5
img = floatimg.create_rgb(width, height)
assert img.get_rgb(0, 0) == [0.0, 0.0, 0.0]
img.put_rgb(0, 0, [127.0, 127.0, 127.0])
assert img.get_rgb(0, 0) == [127.0, 127.0, 127.0]

50
tests/dumps.py Normal file
View File

@ -0,0 +1,50 @@
"""test of file dump functions """
import os
import floatimg
TEST_PATH = os.path.join(os.path.dirname(__file__), "test.dump")
# TODO refactor to fixture
def create_dump():
width, height = 5, 5
img = floatimg.create_rgb(width, height)
# fill it to have something to compare
img.rgb_constant(64.0, 64.0, 64.0)
img.dump_to_file(TEST_PATH)
def test_dump_to_file():
create_dump()
assert os.path.exists(TEST_PATH)
assert os.path.isfile(TEST_PATH)
assert os.path.getsize(TEST_PATH) == 320
os.remove(TEST_PATH)
def test_infos():
create_dump()
floatimg.fileinfos(TEST_PATH) == 5, 5, floatimg.RGB
os.remove(TEST_PATH)
def test_load():
create_dump()
width, height = 5, 5
img = floatimg.create_rgb(width, height)
img.load_from_dump(TEST_PATH)
for y in range(height):
for x in range(width):
assert img.get_rgb(x, y) == [64.0, 64.0, 64.0]
os.remove(TEST_PATH)
def test_create():
create_dump()
img = floatimg.create_from_dump(TEST_PATH)
for y in range(img.height):
for x in range(img.width):
assert img.get_rgb(x, y) == [64.0, 64.0, 64.0]
os.remove(TEST_PATH)