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:
parent
f5200d6fba
commit
1d4e2242cd
14
README.md
14
README.md
@ -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")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -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"
|
@ -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,26 +214,49 @@ 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):
|
|
||||||
"""return witdh, height, img_type triplet read from a dump file"""
|
|
||||||
datas = (ct.c_int * 3)()
|
|
||||||
assert (
|
|
||||||
c_fimg_fileinfos(ct.c_char_p(bytes(fname, encoding="utf8")), ct.pointer(datas))
|
|
||||||
== 0
|
|
||||||
)
|
|
||||||
return datas[:3]
|
|
||||||
|
|
||||||
#######################################################################################################
|
###########################################################################################################
|
||||||
@classmethod
|
def fileinfos(fname):
|
||||||
def create_from_dump(cls, fname):
|
"""return witdh, height, img_type triplet read from a dump file"""
|
||||||
"""Create a new instance from a dump file"""
|
datas = (ct.c_int * 3)()
|
||||||
witdh, height, img_type = cls.fileinfos(fname)
|
assert (
|
||||||
img = cls.create(witdh, height, img_type)
|
c_fimg_fileinfos(ct.c_char_p(bytes(fname, encoding="utf8")), ct.pointer(datas))
|
||||||
img.load_from_dump(fname)
|
== 0
|
||||||
return img
|
)
|
||||||
|
return datas[:3]
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################################################################
|
||||||
|
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"""
|
||||||
|
witdh, height, img_type = fileinfos(fname)
|
||||||
|
img = create(witdh, height, img_type)
|
||||||
|
img.load_from_dump(fname)
|
||||||
|
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
|
||||||
|
@ -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")
|
||||||
|
@ -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
50
tests/dumps.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user