459 lines
16 KiB
Python
459 lines
16 KiB
Python
"""Binding bases"""
|
|
import ctypes as ct
|
|
from enum import Enum
|
|
import logging
|
|
|
|
from floatimg.settings import LIB, PATH_ENCODING
|
|
|
|
############################################################################################################
|
|
# type constants
|
|
GRAY = 1
|
|
RGB = 3
|
|
RGBA = 4 # may not be used
|
|
RGBZ = 99 # may not be used
|
|
|
|
############################################################################################################
|
|
# Contrast enumeration
|
|
class Contrast(Enum):
|
|
NONE = 0
|
|
SQRT = 1
|
|
POW2 = 2
|
|
COS01 = 3
|
|
COS010 = 4
|
|
|
|
|
|
############################################################################################################
|
|
# Type mapping
|
|
class C_FloatImg(ct.Structure):
|
|
"""mapping to the C structure of FloatImg"""
|
|
|
|
pass
|
|
|
|
|
|
C_FloatImg._fields_ = [
|
|
("magic", ct.c_ulong),
|
|
("width", ct.c_int),
|
|
("height", ct.c_int),
|
|
("type", ct.c_int),
|
|
("fval", ct.c_float),
|
|
("count", ct.c_int),
|
|
("R", ct.POINTER(ct.c_float)),
|
|
("G", ct.POINTER(ct.c_float)),
|
|
("B", ct.POINTER(ct.c_float)),
|
|
("A", ct.POINTER(ct.c_float)),
|
|
("reserved", ct.c_int),
|
|
]
|
|
|
|
############################################################################################################
|
|
# declaration of input / output types for C binding of core functions
|
|
|
|
# fimg-core.c
|
|
c_fimgcreate = LIB.fimg_create
|
|
c_fimgcreate.argtypes = (ct.POINTER(C_FloatImg), ct.c_int, ct.c_int, ct.c_int)
|
|
c_fimgcreate.restype = ct.c_int
|
|
|
|
c_fimg_destroy = LIB.fimg_destroy
|
|
c_fimg_destroy.argtypes = (ct.POINTER(C_FloatImg),)
|
|
c_fimg_destroy.restype = ct.c_int
|
|
|
|
c_fimg_clone = LIB.fimg_clone
|
|
c_fimg_clone.argtypes = (ct.POINTER(C_FloatImg), ct.POINTER(C_FloatImg))
|
|
c_fimg_clone.restype = ct.c_int
|
|
|
|
c_fimg_copy_data = LIB.fimg_copy_data
|
|
c_fimg_copy_data.argtypes = (ct.POINTER(C_FloatImg), ct.POINTER(C_FloatImg))
|
|
c_fimg_copy_data.restype = ct.c_int
|
|
|
|
c_fimg_type_is_valid = LIB.fimg_type_is_valid
|
|
c_fimg_type_is_valid.argtypes = (ct.c_int,)
|
|
c_fimg_copy_data.restype = ct.c_int
|
|
|
|
c_fimg_str_type = LIB.fimg_str_type
|
|
c_fimg_str_type.argtypes = (ct.c_int,)
|
|
c_fimg_str_type.restype = ct.c_char_p
|
|
|
|
c_fimg_clear = LIB.fimg_clear
|
|
c_fimg_clear.argtypes = (ct.POINTER(C_FloatImg),)
|
|
c_fimg_clear.restype = ct.c_int
|
|
|
|
c_fimg_plot_rgb = LIB.fimg_plot_rgb
|
|
c_fimg_plot_rgb.argtypes = (
|
|
ct.POINTER(C_FloatImg),
|
|
ct.c_int,
|
|
ct.c_int,
|
|
ct.c_float,
|
|
ct.c_float,
|
|
ct.c_float,
|
|
)
|
|
c_fimg_plot_rgb.restype = ct.c_int
|
|
|
|
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.restype = ct.c_int
|
|
|
|
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.restype = ct.c_int
|
|
|
|
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.restype = ct.c_int
|
|
|
|
# fimg-file.c
|
|
c_fimg_dump_to_file = LIB.fimg_dump_to_file
|
|
c_fimg_dump_to_file.argtypes = (ct.POINTER(C_FloatImg), ct.c_char_p, ct.c_int)
|
|
c_fimg_dump_to_file.restype = ct.c_int
|
|
|
|
c_fimg_load_from_dump = LIB.fimg_load_from_dump
|
|
c_fimg_load_from_dump.argtypes = (ct.c_char_p, ct.POINTER(C_FloatImg))
|
|
c_fimg_load_from_dump.restype = ct.c_int
|
|
|
|
c_fimg_fileinfos = LIB.fimg_fileinfos
|
|
c_fimg_fileinfos.argtypes = (ct.c_char_p, ct.POINTER(ct.c_int * 3))
|
|
c_fimg_fileinfos.restype = ct.c_int
|
|
|
|
c_fimg_add_rgb = LIB.fimg_add_rgb
|
|
c_fimg_add_rgb.argtypes = (
|
|
ct.POINTER(C_FloatImg),
|
|
ct.c_int,
|
|
ct.c_int,
|
|
ct.c_float,
|
|
ct.c_float,
|
|
ct.c_float,
|
|
)
|
|
c_fimg_add_rgb.restype = ct.c_int
|
|
|
|
# # fimg-operators.c
|
|
c_fimg_add_3 = LIB.fimg_add_3
|
|
c_fimg_add_3.argtypes = (
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
)
|
|
c_fimg_add_3.restype = ct.c_int
|
|
|
|
c_fimg_sub_3 = LIB.fimg_sub_3
|
|
c_fimg_sub_3.argtypes = (
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
)
|
|
c_fimg_sub_3.restype = ct.c_int
|
|
|
|
c_fimg_mul_3 = LIB.fimg_mul_3
|
|
c_fimg_mul_3.argtypes = (
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
)
|
|
c_fimg_mul_3.restype = ct.c_int
|
|
|
|
c_fimg_minimum = LIB.fimg_minimum
|
|
c_fimg_minimum.argtypes = (
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
)
|
|
c_fimg_minimum.restype = ct.c_int
|
|
|
|
c_fimg_maximum = LIB.fimg_maximum
|
|
c_fimg_maximum.argtypes = (
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
)
|
|
c_fimg_maximum.restype = ct.c_int
|
|
|
|
# Files i/o
|
|
c_fimg_export_picture = LIB.fimg_export_picture
|
|
c_fimg_export_picture.argtypes = (ct.POINTER(C_FloatImg), ct.c_char_p)
|
|
c_fimg_export_picture.restype = ct.c_int
|
|
|
|
c_fimg_create_from_png = LIB.fimg_create_from_png
|
|
c_fimg_create_from_png.argtypes = (ct.c_char_p, ct.POINTER(C_FloatImg))
|
|
c_fimg_create_from_png.restype = ct.c_int
|
|
|
|
c_fimg_load_from_png = LIB.fimg_load_from_png
|
|
c_fimg_load_from_png.argtypes = (ct.c_char_p, ct.POINTER(C_FloatImg))
|
|
c_fimg_load_from_png.restype = ct.c_int
|
|
|
|
c_fimg_get_maxvalue = LIB.fimg_get_maxvalue
|
|
c_fimg_get_maxvalue.argtypes = (ct.POINTER(C_FloatImg),)
|
|
c_fimg_get_maxvalue.restype = ct.c_float
|
|
|
|
# contrast
|
|
c_fimg_square_root = LIB.fimg_square_root
|
|
c_fimg_square_root.argtypes = (
|
|
ct.POINTER(C_FloatImg),
|
|
ct.POINTER(C_FloatImg),
|
|
ct.c_double,
|
|
)
|
|
c_fimg_square_root.restype = ct.c_int
|
|
|
|
c_fimg_power_2 = LIB.fimg_power_2
|
|
c_fimg_power_2.argtypes = (ct.POINTER(C_FloatImg), ct.POINTER(C_FloatImg), ct.c_double)
|
|
c_fimg_power_2.restype = ct.c_int
|
|
|
|
c_fimg_cos_01 = LIB.fimg_cos_01
|
|
c_fimg_cos_01.argtypes = (ct.POINTER(C_FloatImg), ct.POINTER(C_FloatImg), ct.c_double)
|
|
c_fimg_cos_01.restype = ct.c_int
|
|
|
|
c_fimg_cos_010 = LIB.fimg_cos_010
|
|
c_fimg_cos_010.argtypes = (ct.POINTER(C_FloatImg), ct.POINTER(C_FloatImg), ct.c_double)
|
|
c_fimg_cos_010.restype = ct.c_int
|
|
|
|
# contrast function map
|
|
CONTRAST_FUNC_DICT = {
|
|
Contrast.SQRT: c_fimg_square_root,
|
|
Contrast.POW2: c_fimg_power_2,
|
|
Contrast.COS01: c_fimg_cos_01,
|
|
Contrast.COS010: c_fimg_cos_010,
|
|
}
|
|
|
|
############################################################################################################
|
|
class FloatImg:
|
|
"""
|
|
|
|
Pythonic Object-Oriented encapsulation of floatimg library
|
|
|
|
:attr c_img: an instance of C_FloatImg structure
|
|
:attr c_img: an pointer to the c_img structure
|
|
"""
|
|
|
|
# proxy attributes to the C_FloatImg structure
|
|
# TODO: now more about them ;)
|
|
magic = property(lambda self: self.c_img.magic)
|
|
fval = property(lambda self: self.c_img.fval)
|
|
count = property(lambda self: self.c_img.count)
|
|
|
|
#######################################################################################################
|
|
def __init__(self, c_img):
|
|
self.c_img = c_img
|
|
self.width = c_img.width
|
|
self.height = c_img.height
|
|
self.type_id = c_img.type
|
|
# convenient pointer for later calls to C library
|
|
self.c_img_p = ct.pointer(c_img)
|
|
|
|
#######################################################################################################
|
|
def __str__(self):
|
|
return f"<{self.__class__.__name__} instance {id(self)} width={self.width} height={self.height} type={self.str_type}>"
|
|
|
|
#######################################################################################################
|
|
def destroy(self):
|
|
"""destroy the underlying structure. automattically called at instance destruction"""
|
|
logging.debug("Destroy %s", self)
|
|
assert c_fimg_destroy(self.c_img) == 0
|
|
|
|
#######################################################################################################
|
|
def __del__(self):
|
|
self.destroy()
|
|
|
|
#######################################################################################################
|
|
def clear(self):
|
|
"""clear data"""
|
|
assert c_fimg_clear(self.c_img_p) == 0
|
|
|
|
#######################################################################################################
|
|
def clone(self, copy_data=False):
|
|
"""return a clone of the current instance"""
|
|
c_img_p = C_FloatImg()
|
|
assert c_fimg_clone(self.c_img_p, ct.pointer(c_img_p), int(copy_data)) == 0
|
|
return FloatImg(c_img_p)
|
|
|
|
#######################################################################################################
|
|
def copy_data(self, to_img):
|
|
assert c_fimg_copy_data(self.c_img_p, to_img.c_img_p) == 0
|
|
|
|
#######################################################################################################
|
|
@property
|
|
def str_type(self):
|
|
"""return the type of the image as a string"""
|
|
return c_fimg_str_type(self.c_img.type).decode("utf-8")
|
|
|
|
#######################################################################################################
|
|
def fill(self, color):
|
|
assert c_fimg_rgb_constant(self.c_img_p, *color) == 0
|
|
|
|
#######################################################################################################
|
|
def get(self, x, y):
|
|
"""get r,g,b triplet from a pixel"""
|
|
color = (ct.c_float * 3)()
|
|
assert c_fimg_get_rgb(self.c_img_p, x, y, ct.pointer(color)) == 0
|
|
return color[:3]
|
|
|
|
#######################################################################################################
|
|
def put(self, x, y, color):
|
|
"""put color to a pixel"""
|
|
c_rgb = (ct.c_float * 3)(*color)
|
|
assert c_fimg_put_rgb(self.c_img_p, x, y, ct.pointer(c_rgb)) == 0
|
|
|
|
#######################################################################################################
|
|
def add(self, x, y, color):
|
|
"""add color to a pixel """
|
|
assert c_fimg_add_rgb(self.c_img_p, x, y, *color) == 0
|
|
|
|
#######################################################################################################
|
|
def dump(self, path):
|
|
"""save data to a dump file"""
|
|
# TODO use system encoding instead of utf-8
|
|
assert (
|
|
c_fimg_dump_to_file(
|
|
self.c_img_p, ct.c_char_p(bytes(path, encoding=PATH_ENCODING)), 0
|
|
)
|
|
== 0
|
|
)
|
|
|
|
#######################################################################################################
|
|
def load(self, path):
|
|
"""load data from a dump. size and type have to be compatible"""
|
|
assert (
|
|
c_fimg_load_from_dump(
|
|
ct.c_char_p(bytes(path, encoding=PATH_ENCODING)), self.c_img_p
|
|
)
|
|
== 0
|
|
)
|
|
|
|
#######################################################################################################
|
|
def load_png(self, path):
|
|
"""load data from a png file. size and type have to be compatible"""
|
|
assert (
|
|
c_fimg_load_from_png(
|
|
ct.c_char_p(bytes(path, encoding=PATH_ENCODING)), self.c_img_p
|
|
)
|
|
== 0
|
|
)
|
|
|
|
#######################################################################################################
|
|
def __add__(self, img):
|
|
""" + operator overload"""
|
|
res = self.clone(False)
|
|
assert c_fimg_add_3(self.c_img_p, img.c_img_p, res.c_img_p) == 0
|
|
return res
|
|
|
|
#######################################################################################################
|
|
def __sub__(self, img):
|
|
""" - operator overload"""
|
|
res = self.clone(False)
|
|
assert c_fimg_sub_3(self.c_img_p, img.c_img_p, res.c_img_p) == 0
|
|
return res
|
|
|
|
#######################################################################################################
|
|
def __mul__(self, img):
|
|
""" * operator overload"""
|
|
res = self.clone(False)
|
|
assert c_fimg_mul_3(self.c_img_p, img.c_img_p, res.c_img_p) == 0
|
|
return res
|
|
|
|
#######################################################################################################
|
|
def min(self, img):
|
|
"""return a new image with minimum pixel values per channel between current and parameter"""
|
|
res = self.clone(False)
|
|
# C code inverts comparison
|
|
assert c_fimg_minimum(self.c_img_p, img.c_img_p, res.c_img_p) == 0
|
|
return res
|
|
|
|
#######################################################################################################
|
|
def max(self, img):
|
|
"""return a new image with maximum pixel values per channel between current and parameter"""
|
|
res = self.clone(False)
|
|
# C code inverts comparison
|
|
assert c_fimg_maximum(self.c_img_p, img.c_img_p, res.c_img_p) == 0
|
|
return res
|
|
|
|
#######################################################################################################
|
|
def export(self, path):
|
|
"""export image to path. allowed extension: .fimg, .png, .tiff, .pnm, .fits"""
|
|
assert (
|
|
c_fimg_export_picture(
|
|
self.c_img_p, ct.c_char_p(bytes(path, encoding=PATH_ENCODING)), 0
|
|
)
|
|
== 0
|
|
)
|
|
|
|
#######################################################################################################
|
|
def maxvalue(self):
|
|
"""return the maximum value for any channel in the image"""
|
|
return c_fimg_get_maxvalue(self.c_img_p)
|
|
|
|
#######################################################################################################
|
|
def contrast(self, max_value, contrast_id=Contrast.SQRT, in_place=False):
|
|
""""""
|
|
func = CONTRAST_FUNC_DICT[contrast_id]
|
|
dest = None
|
|
dest_c_img = None
|
|
if not in_place:
|
|
dest = self.clone(False)
|
|
dest_c_img = dest.c_img_p
|
|
assert (func(self.c_img, dest_c_img, max_value)) == 0
|
|
return dest
|
|
|
|
|
|
###########################################################################################################
|
|
def fileinfos(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=PATH_ENCODING)), ct.pointer(datas)
|
|
)
|
|
== 0
|
|
)
|
|
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(path):
|
|
"""Create a FloatImg instance from a dump file"""
|
|
witdh, height, img_type = fileinfos(path)
|
|
img = create(witdh, height, img_type)
|
|
img.load(path)
|
|
return img
|
|
|
|
|
|
###########################################################################################################
|
|
def create_from_png(path):
|
|
"""Create a FloatImg instance from a png file"""
|
|
img = C_FloatImg()
|
|
assert (
|
|
c_fimg_create_from_png(ct.c_char_p(bytes(path, encoding=PATH_ENCODING)), img)
|
|
== 0
|
|
)
|
|
return FloatImg(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
|