diff --git a/README.md b/README.md index f431c6c..45f28a0 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,28 @@ res_img = img.max(img2) ``` +## Contrast operations + +Contrast types are defined in the Contrast enumeration: + +``` +floatimg.Contrast.SQRT +floatimg.Contrast.POW2 +floatimg.Contrast.COS01 +floatimg.Contrast.COS010 +``` + +To apply contrast: + +``` +# Return a new image +res = img.contrast(value, floatimg.Contrast.SQRT) + +# set contrast in-place +img.contrast(value, floatimg.Contrast.SQRT, True) +``` + + ## Development diff --git a/floatimg/__init__.py b/floatimg/__init__.py index b765067..61beb52 100644 --- a/floatimg/__init__.py +++ b/floatimg/__init__.py @@ -10,6 +10,7 @@ from floatimg.image import ( create_from_dump, create_from_png, fileinfos, + Contrast ) __version__ = "0.0.1" diff --git a/floatimg/image.py b/floatimg/image.py index 248001e..4bc696f 100644 --- a/floatimg/image.py +++ b/floatimg/image.py @@ -1,4 +1,7 @@ +"""Binding bases""" import ctypes as ct +from enum import Enum +import logging from floatimg.settings import LIB, PATH_ENCODING @@ -9,6 +12,16 @@ 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): @@ -33,6 +46,8 @@ C_FloatImg._fields_ = [ ############################################################################################################ # 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 @@ -99,6 +114,7 @@ c_fimg_rgb_constant.argtypes = ( ) 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 @@ -122,7 +138,7 @@ c_fimg_add_rgb.argtypes = ( ) c_fimg_add_rgb.restype = ct.c_int -# operators +# # fimg-operators.c c_fimg_add_3 = LIB.fimg_add_3 c_fimg_add_3.argtypes = ( ct.POINTER(C_FloatImg), @@ -180,6 +196,35 @@ 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: """ @@ -212,6 +257,7 @@ class FloatImg: ####################################################################################################### 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 ####################################################################################################### @@ -322,7 +368,6 @@ class FloatImg: 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""" @@ -346,6 +391,18 @@ class FloatImg: """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): diff --git a/tests/contrast.py b/tests/contrast.py new file mode 100644 index 0000000..0c40ba3 --- /dev/null +++ b/tests/contrast.py @@ -0,0 +1,35 @@ +import floatimg +import logging + + +def test_square(): + width, height = 5, 5 + + color = [127.0, 127.0, 127.0] + img = floatimg.create_rgb(width, height) + img.fill(color) + img.put(2, 2, [200, 200, 200]) + + result_dict = { + floatimg.Contrast.SQRT: ( + [225.83180236816406, 225.83180236816406, 225.83180236816406], + [179.9583282470703, 179.9583282470703, 179.9583282470703], + ), + floatimg.Contrast.POW2: ( + [156.8627471923828, 156.8627471923828, 156.8627471923828], + [63.250980377197266, 63.250980377197266, 63.250980377197266], + ), + floatimg.Contrast.COS01: ( + [226.83277893066406, 226.83277893066406, 226.83277893066406], + [126.71460723876953, 126.71460723876953, 126.71460723876953], + ), + floatimg.Contrast.COS010: ( + [100.2235336303711, 100.2235336303711, 100.2235336303711], + [254.99032592773438, 254.99032592773438, 254.99032592773438], + ), + } + + for contrast_type, (center, outer) in result_dict.items(): + res = img.contrast(255, contrast_type) + assert res.get(2, 2) == center + assert res.get(2, 3) == outer