Image_new_item = class Menupullright "_New" "make new things" { Image_black_item = class Menuaction "_Image" "make a new image" { format_names = [ "8-bit unsigned int - UCHAR", // 0 "8-bit signed int - CHAR", // 1 "16-bit unsigned int - USHORT", // 2 "16-bit signed int - SHORT", // 3 "32-bit unsigned int - UINT", // 4 "32-bit signed int - INT", // 5 "32-bit float - FLOAT", // 6 "64-bit complex - COMPLEX", // 7 "64-bit float - DOUBLE", // 8 "128-bit complex - DPCOMPLEX" // 9 ]; action = class Image _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; nbands = Expression "Image bands" 1; format_option = Option "Image format" format_names 0; type_option = Option_enum "Image type" Image_type.type_names "B_W"; pixel = Expression "Pixel value" 0; _result = image_new (to_real nwidth) (to_real nheight) (to_real nbands) (to_real format_option) Image_coding.NOCODING type_option.value_thing pixel.expr 0 0; } } Image_new_from_image_item = class Menuaction "_From Image" "make a new image based on image x" { action x = class Image _result { _vislevel = 3; pixel = Expression "Pixel value" 0; _result = image_new x.width x.height x.bands x.format x.coding x.type pixel.expr x.xoffset x.yoffset; } } Image_region_item = class Menupullright "_Region on Image" "make a new region on an image" { Region_item = class Menuaction "_Region" "make a region on an image" { action image = scope.Region_relative image 0.25 0.25 0.5 0.5; } Mark_item = class Menuaction "_Point" "make a point on an image" { action image = scope.Mark_relative image 0.5 0.5; } Arrow_item = class Menuaction "_Arrow" "make an arrow on an image" { action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; } HGuide_item = class Menuaction "_Horizontal Guide" "make a horizontal guide on an image" { action image = scope.HGuide image 0.5; } VGuide_item = class Menuaction "_Vertical Guide" "make a vertical guide on an image" { action image = scope.VGuide image 0.5; } sep1 = Menuseparator; Move_item = class Menuaction "From Region" "new region on image using existing region as a guide" { action a b = map_binary process a b { process a b = x.Region target x.left x.top x.width x.height, is_Region x = x.Arrow target x.left x.top x.width x.height, is_Arrow x = error "bad arguments to region-from-region" { // prefer image then region compare a b = false, !is_Image a && is_Image b = false, is_Region a && !is_Region b = true; [target, x] = sortc compare [a, b]; } } } } } Image_convert_to_image_item = class Menuaction "Con_vert to Image" "convert anything to an image" { action x = to_image x; } Image_number_format_item = class Menupullright "_Format" "convert numeric format" { U8_item = class Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { action x = map_unary cast_unsigned_char x; } U16_item = class Menuaction "1_6 bit unsigned" "convert to unsigned 16 bit [0, 65535]" { action x = map_unary cast_unsigned_short x; } U32_item = class Menuaction "_32 bit unsigned" "convert to unsigned 32 bit [0, 4294967295]" { action x = map_unary cast_unsigned_int x; } sep1 = Menuseparator; S8_item = class Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { action x = map_unary cast_signed_char x; } S16_item = class Menuaction "16 b_it signed" "convert to signed 16 bit [-32768, 32767]" { action x = map_unary cast_signed_short x; } S32_item = class Menuaction "32 bi_t signed" "convert to signed 32 bit [-2147483648, 2147483647]" { action x = map_unary cast_signed_int x; } sep2 = Menuseparator; Float_item = class Menuaction "_Single precision float" "convert to IEEE 32 bit float" { action x = map_unary cast_float x; } Double_item = class Menuaction "_Double precision float" "convert to IEEE 64 bit float" { action x = map_unary cast_double x; } sep3 = Menuseparator; Scmplxitem = class Menuaction "Single _precision complex" "convert to 2 x IEEE 32 bit float" { action x = map_unary cast_complex x; } Dcmplx_item = class Menuaction "Double p_recision complex" "convert to 2 x IEEE 64 bit float" { action x = map_unary cast_double_complex x; } } Image_header_item = class Menupullright "_Header" "do stuff to the image header" { Image_get_item = class Menupullright "_Get" "get header fields" { // the header fields we can get fields = class { type = 0; width = 1; height = 2; format = 3; bands = 4; xres = 5; yres = 6; xoffset = 7; yoffset = 8; coding = 9; field_names = Enum [ $width => width, $height => height, $bands => bands, $format => format, $type => type, $xres => xres, $yres => yres, $xoffset => xoffset, $yoffset => yoffset, $coding => coding ]; field_option name = Option_enum (_ "Field") field_names name; field_funcs = Table [ [type, get_type], [width, get_width], [height, get_height], [format, get_format], [bands, get_bands], [xres, get_xres], [yres, get_yres], [xoffset, get_xoffset], [yoffset, get_yoffset], [coding, get_coding] ]; } get_field field_name x = class _result { _vislevel = 3; field = fields.field_option field_name; _result = map_unary (Real @ fields.field_funcs.lookup 0 1 field.value_thing) x; } Width_item = class Menuaction "_Width" "get width" { action x = get_field "width" x; } Height_item = class Menuaction "_Height" "get height" { action x = get_field "height" x; } Bands_item = class Menuaction "_Bands" "get bands" { action x = get_field "bands" x; } Format_item = class Menuaction "_Format" "get format" { action x = get_field "format" x; } Type_item = class Menuaction "_Type" "get type" { action x = get_field "type" x; } Xres_item = class Menuaction "_Xres" "get X resolution" { action x = get_field "xres" x; } Yres_item = class Menuaction "_Yres" "get Y resolution" { action x = get_field "yres" x; } Xoffset_item = class Menuaction "X_offset" "get X offset" { action x = get_field "xoffset" x; } Yoffset_item = class Menuaction "Yo_ffset" "get Y offset" { action x = get_field "yoffset" x; } Coding_item = class Menuaction "_Coding" "get coding" { action x = get_field "coding" x; } sep1 = Menuseparator; Custom_item = class Menuaction "C_ustom" "get any header field" { action x = class _result { _vislevel = 3; field = String "Field" "Xsize"; parse = Option "Parse" [ "No parsing", "Parse string as integer", "Parse string as real", "Parse string as hh:mm:ss" ] 0; _result = map_unary (wrap @ process @ get_header field.value) x { parse_str parse str = parse (split is_space str)?0; parse_field name cast parse x = cast x, is_number x = parse_str parse x, is_string x = error ("not " ++ name); get_int = parse_field "int" cast_signed_int parse_int; get_float = parse_field "float" cast_float parse_float; get_time = parse_field "hh:mm:ss" cast_signed_int parse_time; wrap x = Real x, is_real x = Vector x, is_real_list x = Image x, is_image x = Bool x, is_bool x = Matrix x, is_matrix x = String "String" x, is_string x = List x, is_list x = x; process = [ id, get_int, get_float, get_time ]?parse; } } } } sep1 = Menuseparator; Image_set_meta_item = class Menuaction "_Set" "set image metadata" { action x = class _result { _vislevel = 3; fname = String "Field" "field-name"; val = Expression "Value" 42; _result = map_unary process x { process image = set_header fname.value val.expr image; } } } Image_edit_header_item = class Menuaction "_Edit" "change advisory header fields of image" { type_names = Image_type.type_names; all_names = sort (map (extract 0) type_names.value); get_prop has get def x = get x, has x = def; action x = class _result { _vislevel = 3; nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); type_option = Option_enum "Image type" Image_type.type_names (Image_type.type_names.get_name type) { type = x.type, is_Image x = Image_type.MULTIBAND; } _result = map_unary process x { process image = Image (im_copy_set image.value type_option.value_thing (to_real nxres) (to_real nyres) (to_real nxoff) (to_real nyoff)); } } } } Image_cache_item = class Menuaction "C_ache" "cache calculated image pixels" { action x = class _result { _vislevel = 3; tile_width = Number "Tile width" 128; tile_height = Number "Tile height" 128; max_tiles = Number "Maximum number of tiles to cache" (-1); _result = map_unary process x { process image = cache (to_real tile_width) (to_real tile_height) (to_real max_tiles) image; } } } #separator Image_levels_item = class Menupullright "_Levels" "change image levels" { Scale_item = class Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { action x = map_unary scale x; } Linear_item = class Menuaction "_Linear" "linear transform of image levels" { action x = class _result { _vislevel = 3; scale = Scale "Scale" 0.001 3 1; offset = Scale "Offset" (-128) 128 0; _result = map_unary adj x { adj x // only force back to input type if this is a thing // with a type ... so we work for Colour / Matrix etc. = clip2fmt x.format x', has_member "format" x = x' { x' = x * scale + offset; } } } } Gamma_item = class Menuaction "_Power" "power transform of image levels (gamma)" { action x = class _result { _vislevel = 3; gamma = Scale "Gamma" 0.001 4 1; image_maximum_hint = "You may need to change image_maximum if " ++ "this is not an 8 bit image"; im_mx = Expression "Image maximum" mx { mx = Image_format.maxval x.format, has_format x = 255; } _result = map_unary gam x { gam x = clip2fmt (get_format x) x', has_format x = x' { x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; } } } } Tone_item = class Menuaction "_Tone Curve" "adjust tone curve" { action x = class _result { _vislevel = 3; b = Scale "Black point" 0 100 0; w = Scale "White point" 0 100 100; sp = Scale "Shadow point" 0.1 0.3 0.2; mp = Scale "Mid-tone point" 0.4 0.6 0.5; hp = Scale "Highlight point" 0.7 0.9 0.8; sa = Scale "Shadow adjust" (-15) 15 0; ma = Scale "Mid-tone adjust" (-30) 30 0; ha = Scale "Highlight adjust" (-15) 15 0; curve = tone_build x.format b w sp mp hp sa ma ha; _result = map_unary (hist_map curve) x; } } } Image_transform_item = class Menupullright "_Transform" "transform images" { Rotate_item = class Menupullright "Ro_tate" "rotate image" { Fixed_item = class Menupullright "_Fixed" "clockwise rotation by fixed angles" { rotate_widget default x = class _result { _vislevel = 3; angle = Option "Rotate by" [ "Don't rotate", "90 degrees clockwise", "180 degrees", "90 degrees anticlockwise" ] default; _result = map_unary process x { process in = [ in, rot90 in, rot180 in, rot270 in ] ? angle; } } Rot90_item = class Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { action x = rotate_widget 1 x; } Rot180_item = class Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { action x = rotate_widget 2 x; } Rot270_item = class Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { action x = rotate_widget 3 x; } } Free_item = class Menuaction "_Free" "clockwise rotation by any angle" { action x = class _result { _vislevel = 3; angle = Scale "Angle" (-180) 180 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = rotate interp angle image; } } } Straighten_item = class Menuaction "_Straighten" ("smallest rotation that makes an arrow either horizontal " ++ "or vertical") { action x = class _result { _vislevel = 3; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary straighten x { straighten arrow = rotate interp angle'' arrow.image { x = arrow.width; y = arrow.height; angle = im (polar (x, y)); angle' = angle - 360, angle > 315 = angle - 180, angle > 135 = angle; angle'' = -angle', angle' >= (-45) && angle' < 45 = 90 - angle'; } } } } } Flip_item = class Menupullright "_Flip" "mirror left/right or up/down" { Left_right_item = class Menuaction "_Left Right" "mirror object left/right" { action x = map_unary fliplr x; } Top_bottom_item = class Menuaction "_Top Bottom" "mirror object top/bottom" { action x = map_unary fliptb x; } } Resize_item = class Menupullright "_Resize" "change image size" { Scale_item = class Menuaction "_Scale" "scale image size by a factor" { action x = class _result { _vislevel = 3; xfactor = Expression "Horizontal scale factor" 1; yfactor = Expression "Vertical scale factor" 1; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp xfactor yfactor image; } } } Size_item = class Menuaction "_Size To" "resize to a fixed size" { action x = class _result { _vislevel = 3; which = Option "Resize axis" [ "Shortest", "Longest", "Horizontal", "Vertical" ] 0; size = Expression "Resize to (pixels)" 128; aspect = Toggle "Break aspect ratio" false; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { process image = resize interp h v image, aspect = resize interp fac fac image { xfac = to_real size / image.width; yfac = to_real size / image.height; max_factor = [xfac, 1], xfac > yfac = [1, yfac]; min_factor = [xfac, 1], xfac < yfac = [1, yfac]; [h, v] = [ max_factor, min_factor, [xfac, 1], [1, yfac]]?which; fac = h, v == 1 = v; } } } } Size_within_item = class Menuaction "Size _Within" "size to fit within a rectangle" { action x = class _result { _vislevel = 3; // the rects we size to fit within _rects = [ [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], [1280, 1024], [1024, 768], [800, 600], [640, 480] ]; within = Option "Fit within (pixels)" ( [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ ["Custom"] ) 4; custom_width = Expression "Custom width" 1000; custom_height = Expression "Custom height" 1000; size = Option "Page size" [ "Full page", "Half page", "Quarter page" ] 0; interp = Interpolate_picker Interpolate_type.BILINEAR; _result = map_unary process x { xdiv = [1, 2, 2]?size; ydiv = [1, 1, 2]?size; allrect = _rects ++ [ [custom_width.expr, custom_height.expr] ]; [width, height] = allrect?within; process x = resize interp fac fac x, fac < 1 = x { xfac = (width / xdiv) / x.width; yfac = (height / ydiv) / x.height; fac = min_pair xfac yfac; } } } } Resize_canvas_item = class Menuaction "_Canvas" "change size of surrounding image" { action x = class _result { _vislevel = 3; // try to guess a sensible size for the new image _guess_size = x.rect, is_Image x = Rect 0 0 100 100; nwidth = Expression "New width (pixels)" _guess_size.width; nheight = Expression "New height (pixels)" _guess_size.height; bgcolour = Expression "Background colour" 0; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_unary process x { process image = insert_noexpand xp yp image background { width = image.width; height = image.height; coding = image.coding; bands = 3, coding == Image_coding.LABPACK = image.bands; format = Image_format.FLOAT, coding == Image_coding.LABPACK = image.format; type = image.type; // placement vectors ... left, centre, right xposv = [0, to_real nwidth / 2 - width / 2, to_real nwidth - width]; yposv = [0, to_real nheight / 2 - height / 2, to_real nheight - height]; xp = left, position == 9 = xposv?((int) (position % 3)); yp = top, position == 9 = yposv?((int) (position / 3)); background = image_new nwidth nheight bands format coding type bgcolour.expr 0 0; } } } } } Image_perspective_item = Perspective_item; Image_rubber_item = class Menupullright "Ru_bber Sheet" "automatically warp images to superposition" { rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; rubber_order = Option "Order" ["0", "1", "2", "3"] 1; rubber_wrap = Toggle "Wrap image edges" false; // a transform ... a matrix, plus the size of the image the // matrix was made for Transform matrix image_width image_height = class matrix { // scale a transform ... if it worked for a m by n image, make // it work for a (m * xfac) by (y * yfac) image rescale xfac yfac = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) (image_width * xfac) (image_height * yfac) { facs = [ [xfac, yfac], [1, 1], [1, 1], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac], [1 / xfac, 1 / yfac] ]; } } // yuk!!!! fix is_instanceof to not need absolute names is_Transform = is_instanceof "Image_transform_item.Image_rubber_item.Transform"; Find_item = class Menuaction "_Find" ("find a transform which will map sample image onto " ++ "reference") { action reference sample = class _trn { _vislevel = 3; // controls order = rubber_order; interp = rubber_interp; wrap = rubber_wrap; max_err = Expression "Maximum error" 0.3; max_iter = Expression "Maximum iterations" 10; // transform [sample', trn, err] = transform_search max_err max_iter order interp wrap sample reference; transformed_image = Image sample'; _trn = Transform trn reference.width reference.height; final_error = err; } } Apply_item = class Menuaction "_Apply" "apply a transform to an image" { action a b = class _result { _vislevel = 3; // controls interp = rubber_interp; wrap = rubber_wrap; _result = map_binary trans a b { trans a b = transform interp wrap t' i { // get the transform arg first [i, t] = sortc (const is_Transform) [a, b]; t' = t.rescale (i.width / t.image_width) (i.height / t.image_height); } } } } } sep1 = Menuseparator; Match_item = class Menuaction "_Linear Match" "rotate and scale one image to match another" { action x y = class _result { _vislevel = 3; // try to find an image ... for a group, get the first item find_image x = x, is_Image x = find_image x?0, is_list x = find_image x.value, is_class x && has_value x = error "unable to find image"; _a = find_image x; _b = find_image y; ap1 = Mark_relative _a 0.5 0.25; bp1 = Mark_relative _b 0.5 0.25; ap2 = Mark_relative _a 0.5 0.75; bp2 = Mark_relative _b 0.5 0.75; refine = Toggle "Refine selected tie-points" false; lock = Toggle "No resize" false; _result = map_binary process x y { process a b = Image b''' { _prefs = Workspaces.Preferences; window = _prefs.MOSAIC_WINDOW_SIZE; object = _prefs.MOSAIC_OBJECT_SIZE; a' = a.value; b' = b.value; b'' = clip2fmt a.format b'; // return p2 ... if lock is set, return a p2 a standard // distance along the vector joining p1 and p2 norm p1 p2 = Rect left' top' 0 0, lock = p2 { v = (p2.left - p1.left, p2.top - p1.top); // 100000 to give precision since we pass points as // ints to match n = 100000 * sign v; left' = p1.left + re n; top' = p1.top + im n; } ap2'' = norm ap1 ap2; bp2'' = norm bp1 bp2; b''' = im_match_linear_search a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top object window, // we can't search if lock is on refine && !lock = im_match_linear a' b'' ap1.left ap1.top bp1.left bp1.top ap2''.left ap2''.top bp2''.left bp2''.top; } } } } Image_perspective_match_item = Perspective_match_item; } Image_band_item = class Menupullright "_Band" "manipulate image bands" { // like extract_bands, but return [] for zero band image // makes compose a bit simpler exb b n x = [], to_real n == 0 = extract_bands b n x; Extract_item = class Menuaction "_Extract" "extract bands from image" { action x = class _result { _vislevel = 3; first = Expression "Extract from band" 0; number = Expression "Extract this many bands" 1; _result = map_unary (exb first number) x; } } Insert_item = class Menuaction "_Insert" "insert bands into image" { action x y = class _result { _vislevel = 3; first = Expression "Insert at position" 0; _result = map_binary process x y { process im1 im2 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 { f = to_real first; b = im1.bands; } } } } Delete_item = class Menuaction "_Delete" "delete bands from image" { action x = class _result { _vislevel = 3; first = Expression "Delete from band" 0; number = Expression "Delete this many bands" 1; _result = map_unary process x { process im = exb 0 f im ++ exb (f + n) (b - (f + n)) im { f = to_real first; n = to_real number; b = im.bands; } } } } Bandwise_item = Image_join_item.Bandwise_item; sep1 = Menuseparator; To_dimension_item = class Menuaction "To D_imension" "convert bands to width or height" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = foldl1 [join_lr, join_tb]?orientation (bandsplit im); } } } To_bands_item = class Menuaction "To B_ands" "turn width or height to bands" { action x = class _result { _vislevel = 3; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; _result = map_unary process x { process im = bandjoin (map extract_column [0 .. im.width - 1]), orientation == 0 = bandjoin (map extract_row [0 .. im.height - 1]) { extract_column n = extract_area n 0 1 im.height im; extract_row n = extract_area 0 n im.width 1 im; } } } } } Image_crop_item = class Menuaction "_Crop" "extract a rectangular area from an image" { action x = class _result { _vislevel = 3; // try to find the image geometry ... don't bother trying to look // inside groups though _geo = x.rect, is_Image x = Rect 0 0 100 100; l = Expression "Crop left" ((int) (_geo.left + _geo.width / 4)); t = Expression "Crop top" ((int) (_geo.top + _geo.height / 4)); w = Expression "Crop width" (max_pair 1 ((int) (_geo.width / 2))); h = Expression "Crop height" (max_pair 1 ((int) (_geo.height / 2))); _result = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] { extract im l t w h = extract_area left' top' width' height' im { width' = min_pair (to_real w) im.width; height' = min_pair (to_real h) im.height; left' = range 0 (to_real l) (im.width - width'); top' = range 0 (to_real t) (im.height - height'); } } } } Image_insert_item = class Menuaction "_Insert" "insert a small image into a large image" { action a b = insert_position, is_Group a || is_Group b = insert_area { insert_area = class _result { _check_args = [ [a, "a", check_Image], [b, "b", check_Image] ]; _vislevel = 3; // sort to get smallest first _pred x y = x.width * x.height < y.width * y.height; [_a', _b'] = sortc _pred [a, b]; place = Area _b' left top width height { // be careful in case b is smaller than a left = max_pair 0 ((_b'.width - _a'.width) / 2); top = max_pair 0 ((_b'.height - _a'.height) / 2); width = min_pair _a'.width _b'.width; height = min_pair _a'.height _b'.height; } _result = insert_noexpand place.left place.top (clip2fmt _b'.format a'') _b' { a'' = extract_area 0 0 place.width place.height _a'; } } insert_position = class _result { _vislevel = 3; position = Option "Position" [ "North-west", "North", "North-east", "West", "Centre", "East", "South-west", "South", "South-east", "Specify in pixels" ] 4; left = Expression "Pixels from left" 0; top = Expression "Pixels from top" 0; _result = map_binary insert a b { insert a b = insert_noexpand left top (clip2fmt b.format a) b, position == 9 = insert_noexpand xp yp (clip2fmt b.format a) b { xr = b.width - a.width; yr = b.height - a.height; xp = [0, xr / 2, xr]?((int) (position % 3)); yp = [0, yr / 2, yr]?((int) (position / 3)); } } } } } Image_select_item = Select_item; Image_draw_item = class Menupullright "_Draw" "draw lines, circles, rectangles, floods" { Line_item = class Menuaction "_Line" "draw line on image" { action x = class _result { _vislevel = 3; x1 = Expression "Start x" 0; y1 = Expression "Start y" 0; x2 = Expression "End x" 100; y2 = Expression "End y" 100; ink = Expression "Ink" [0]; _result = map_unary line x { line im = draw_line x1.expr y1.expr x2.expr y2.expr ink.expr im; } } } Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { action x = class _result { _vislevel = 3; left = Expression "Left" 50; top = Expression "Top" 50; width = Expression "Width" 100; height = Expression "Height" 100; fill = Toggle "Fill" true; ink = Expression "Ink" [0]; _result = map_unary rect x { rect im = draw_rect left.expr top.expr width.expr height.expr fill.value ink.expr im; } } } Circle_item = class Menuaction "_Circle" "draw circle on image" { action x = class _result { _vislevel = 3; cx = Expression "Centre x" 100; cy = Expression "Centre y" 100; r = Expression "Radius" 50; fill = Toggle "Fill" true; ink = Expression "Ink" [0]; _result = map_unary circle x { circle im = draw_circle cx.expr cy.expr r.expr fill.value ink.expr im; } } } Flood_item = class Menuaction "_Flood" "flood bounded area of image" { action x = class _result { _vislevel = 3; startx = Expression "Start x" 100; starty = Expression "Start y" 100; edge = Option "Flood while" [ "Not equal to ink", "Equal to start point" ] 0; // pick a default ink that won't flood, if we can ink = Expression "Ink" default_ink { default_ink = [0], ! has_image x = pixel; pixel = map mean (bandsplit (extract_area left top 1 1 im)); left = startx.expr; top = starty.expr; im = get_image x; } _result = map_unary flood x { flood im = draw_flood left top ink.expr im, edge == 0 = draw_flood_blob left top ink.expr im { left = startx.expr; top = starty.expr; } } } } } Image_join_item = class Menupullright "_Join" "join two or more images together" { Bandwise_item = class Menuaction "_Bandwise" "join two images bandwise" { action a b = join a b; } sep1 = Menuseparator; join_lr shim bg align a b = im2 { w = a.width + b.width + shim; h = max_pair a.height b.height; back = image_new w h a.bands a.format a.coding a.type bg 0 0; ya = [0, max_pair 0 ((b.height - a.height)/2), max_pair 0 (b.height - a.height)]; yb = [0, max_pair 0 ((a.height - b.height)/2), max_pair 0 (a.height - b.height)]; im1 = insert_noexpand 0 ya?align a back; im2 = insert_noexpand (a.width + shim) yb?align b im1; } join_tb shim bg align a b = im2 { w = max_pair a.width b.width; h = a.height + b.height + shim; back = image_new w h a.bands a.format a.coding a.type bg 0 0; xa = [0, max_pair 0 ((b.width - a.width)/2), max_pair 0 (b.width - a.width)]; xb = [0, max_pair 0 ((a.width - b.width)/2), max_pair 0 (a.width - b.width)]; im1 = insert_noexpand xa?align 0 a back; im2 = insert_noexpand xb?align (a.height + shim) b im1; } halign_names = ["Top", "Centre", "Bottom"]; valign_names = ["Left", "Centre", "Right"]; Left_right_item = class Menuaction "_Left to Right" "join two images left-right" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" halign_names 1; _result = map_binary (join_lr shim.value bg_colour.expr align.value) a b; } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom" { action a b = class _result { _vislevel = 3; shim = Scale "Spacing" 0 100 0; bg_colour = Expression "Background colour" 0; align = Option "Alignment" valign_names 1; _result = map_binary (join_tb shim.value bg_colour.expr align.value) a b; } } sep2 = Menuseparator; Array_item = class Menuaction "_Array" "join a list of lists of images into a single image" { action x = class _result { _vislevel = 3; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; // we can't use map_unary since chop-into-tiles returns a group of // groups and we want to be able to reassemble that // TODO: chop-into-tiles should return an array class which // displays as group but does not have the looping behaviour? _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list x)); } } ArrayFL_item = class Menuaction "_Array from List" "join a list of images into a single image" { action x = class _result { _vislevel = 3; ncol = Number "Max. Number of Columns" 1; hshim = Scale "Horizontal spacing" (-100) (100) 0; vshim = Scale "Vertical spacing" (-100) (100) 0; bg_colour = Expression "Background colour" 0; halign = Option "Horizontal alignment" valign_names 1; valign = Option "Vertical alignment" halign_names 1; _l = split_lines ncol.value x.value, is_Group x = split_lines ncol.value x; _result = (image_set_origin 0 0 @ foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ map (foldl1 (join_lr hshim.value bg_colour.expr valign.value))) (to_list (to_list _l)); } } } Image_tile_item = class Menupullright "Til_e" "tile an image across and down" { tile_widget default_type x = class _result { _vislevel = 3; across = Expression "Tiles across" 2; down = Expression "Tiles down" 2; repeat = Option "Tile type" ["Replicate", "Four-way mirror"] default_type; _result = map_unary process x { process image = tile across down image, repeat == 0 = tile across down image'' { image' = insert image.width 0 (fliplr image) image; image'' = insert 0 image.height (fliptb image') image'; } } } Replicate_item = class Menuaction "_Replicate" "replicate image across and down" { action x = tile_widget 0 x; } Fourway_item = class Menuaction "_Four-way Mirror" "four-way mirror across and down" { action x = tile_widget 1 x; } Chop_item = class Menuaction "_Chop Into Tiles" "slice an image into tiles" { action x = class _result { _vislevel = 3; tile_width = Expression "Tile width" 100; tile_height = Expression "Tile height" 100; hoverlap = Expression "Horizontal overlap" 0; voverlap = Expression "Vertical overlap" 0; _result = map_unary (Group @ map Group @ process) x { process x = imagearray_chop tile_width tile_height hoverlap voverlap x; } } } } #separator Pattern_images_item = class Menupullright "_Patterns" "make a variety of useful patterns" { Grey_item = class Menuaction "Grey _Ramp" "make a smooth grey ramp" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; orientation = Option "Orientation" [ "Horizontal", "Vertical" ] 0; foption = Option "Format" ["8 bit", "float"] 0; _result = Image im { gen = im_grey, foption == 0 = im_fgrey; w = to_real nwidth; h = to_real nheight; im = gen w h, orientation == 0 = rot90 (gen h w); } } } Xy_item = class Menuaction "_XY Image" "make a two band image whose pixel values are their coordinates" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; _result = Image (make_xy nwidth nheight); } } Gaussian_item = class Menuaction "Gaussian _Noise" "make an image of gaussian noise" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; mean = Scale "Mean" 0 255 128; deviation = Scale "Deviation" 0 128 50; _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) mean.value deviation.value); } } Fractal_item = class Menuaction "_Fractal" "make a fractal image" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; dimension = Scale "Dimension" 2.001 2.999 2.001; _result = Image (im_fractsurf (to_real nsize) dimension.value); } } Checkerboard_item = class Menuaction "_Checkerboard" "make a checkerboard image" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hpsize = Expression "Horizontal patch size" 8; vpsize = Expression "Vertical patch size" 8; hpoffset = Expression "Horizontal patch offset" 0; vpoffset = Expression "Vertical patch offset" 0; _result = Image (xstripes ^ ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hpoffset; ypixels = pixels?1 + to_real vpoffset; make_stripe pix swidth = pix % (swidth * 2) >= swidth; xstripes = make_stripe xpixels (to_real hpsize); ystripes = make_stripe ypixels (to_real vpsize); } } } Grid_item = class Menuaction "Gri_d" "make a grid" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; hspace = Expression "Horizontal line spacing" 8; vspace = Expression "Vertical line spacing" 8; thick = Expression "Line thickness" 1; hoff = Expression "Horizontal grid offset" 4; voff = Expression "Vertical grid offset" 4; _result = Image (xstripes | ystripes) { pixels = make_xy nwidth nheight; xpixels = pixels?0 + to_real hoff; ypixels = pixels?1 + to_real voff; make_stripe pix swidth = pix % swidth < to_real thick; xstripes = make_stripe xpixels (to_real hspace); ystripes = make_stripe ypixels (to_real vspace); } } } Text_item = class Menuaction "_Text" "make a bitmap of some text" { action = class _result { _vislevel = 3; text = String "Text to paint" "Hello world!"; font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; wrap = Expression "Wrap text at" 500; align = Option "Alignment" [ "Left", "Centre", "Right" ] 0; dpi = Expression "DPI" 300; _result = Image (im_text text.value font.value (to_real wrap) align.value (to_real dpi)); } } New_CIELAB_slice_item = class Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; L = Scale "L value" 0 100 50; _result = Image (lab_slice (to_real nsize) L.value); } } sense_option = Option "Sense" [ "Pass", "Reject" ] 0; build fn size = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) (im_create_fmask size size); New_ideal_item = class Menupullright "_Ideal Fourier Mask" "make various ideal Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f sense.value fc.value 0 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 6) fc.value rw.value 0 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "ideal Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 12) fcx.value fcy.value r.value 0 0; } } } } New_gaussian_item = class Menupullright "_Gaussian Fourier Mask" "make various Gaussian Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 4) fc.value ac.value 0 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 10) fc.value rw.value ac.value 0 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Gaussian Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; _result = build param (to_real nsize) { param f = f (sense.value + 16) fcx.value fcy.value r.value ac.value 0; } } } } New_butterworth_item = class Menupullright "_Butterworth Fourier Mask" "make various Butterworth Fourier filter masks" { High_low_item = class Menuaction "_High or Low Pass" ("make a mask image for a highpass/lowpass " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 2) order.value fc.value ac.value 0 0; } } } Ring_item = class Menuaction "_Ring Pass or Ring Reject" ("make a mask image for an ring pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fc = Scale "Frequency cutoff" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; rw = Scale "Ring width" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 8) order.value fc.value rw.value ac.value 0; } } } Band_item = class Menuaction "_Band Pass or Band Reject" ("make a mask image for a band pass/reject " ++ "Butterworth Fourier filter") { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; sense = sense_option; fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; r = Scale "Radius" 0.01 0.99 0.5; ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; order = Scale "Order" 1 10 2; _result = build param (to_real nsize) { param f = f (sense.value + 14) order.value fcx.value fcy.value r.value ac.value; } } } } } Test_images_item = class Menupullright "Test I_mages" "make a variety of test images" { Eye_item = class Menuaction "_Spatial Response" "image for testing the eye's spatial response" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; nheight = Expression "Image height (pixels)" 64; factor = Scale "Factor" 0.001 1 0.2; _result = Image (im_eye (to_real nwidth) (to_real nheight) factor.value); } } Zone_plate = class Menuaction "_Zone Plate" "make a zone plate" { action = class _result { _vislevel = 3; nsize = Expression "Image size (pixels)" 64; _result = Image (im_zone (to_real nsize)); } } Frequency_test_chart_item = class Menuaction "_Frequency Testchart" "make a black/white frequency test pattern" { action = class _result { _vislevel = 3; nwidth = Expression "Image width (pixels)" 64; sheight = Expression "Strip height (pixels)" 10; waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; _result = imagearray_assemble 0 0 (transpose [strips]) { freq_slice wave = Image (sin (grey / wave) > 0); strips = map freq_slice waves.expr; grey = im_fgrey (to_real nwidth) (to_real sheight) * 360 * (to_real nwidth); } } } CRT_test_chart_item = class Menuaction "CRT _Phosphor Chart" "make an image for measuring phosphor colours" { action = class _result { _vislevel = 3; brightness = Scale "Brightness" 0 255 200; psize = Expression "Patch size (pixels)" 32; _result = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) { black = image_new (to_real psize) (to_real psize) 1 Image_format.FLOAT Image_coding.NOCODING Image_type.B_W 0 0 0; notblack = black + brightness; green = black ++ notblack ++ black; red = notblack ++ black ++ black; blue = black ++ black ++ notblack; white = notblack ++ notblack ++ notblack; } } } Greyscale_chart_item = class Menuaction "_Greyscale" "make a greyscale" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.B_W (clip2fmt Image_format.UCHAR wedge)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } } } } CMYK_test_chart_item = class Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { action = class _result { _vislevel = 3; pwidth = Expression "Patch width" 8; pheight = Expression "Patch height" 8; npatches = Expression "Number of patches" 16; _result = Image (image_set_type Image_type.CMYK (clip2fmt Image_format.UCHAR strips)) { wedge = 255 / (to_real npatches - 1) * (int) (strip?0 / to_real pwidth) { strip = make_xy (to_real pwidth * to_real npatches) pheight; } black = wedge * 0; C = wedge ++ black ++ black ++ black; M = black ++ wedge ++ black ++ black; Y = black ++ black ++ wedge ++ black; K = black ++ black ++ black ++ wedge; strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; } } } Colour_atlas_item = class Menuaction "_Colour Atlas" "make a grid of patches grouped around a colour" { action = class _result { _vislevel = 3; start = Colour_picker "Lab" [50,0,0]; nstep = Expression "Number of steps" 9; ssize = Expression "Step size" 10; psize = Expression "Patch size" 32; sepsize = Expression "Separator size" 4; _result = colour_transform_to (get_type start) blocks''' { size = (to_real nstep * 2 + 1) * to_real psize - to_real sepsize; xy = make_xy size size; xy_grid = (xy % to_real psize) < (to_real psize - to_real sepsize); grid = xy_grid?0 & xy_grid?1; blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); lab_start = colour_transform_to Image_type.LAB start; blocks' = blocks - to_real nstep * to_real ssize + Vector (tl lab_start.value); blocks'' = hd lab_start.value ++ Image blocks'; blocks''' = image_set_type Image_type.LAB blocks'', Image grid = 0; } } } }