"""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