1Image_new_item = class Menupullright "_New" "make new things" { 2 Image_black_item = class Menuaction "_Image" "make a new image" { 3 format_names = [ 4 "8-bit unsigned int - UCHAR", // 0 5 "8-bit signed int - CHAR", // 1 6 "16-bit unsigned int - USHORT", // 2 7 "16-bit signed int - SHORT", // 3 8 "32-bit unsigned int - UINT", // 4 9 "32-bit signed int - INT", // 5 10 "32-bit float - FLOAT", // 6 11 "64-bit complex - COMPLEX", // 7 12 "64-bit float - DOUBLE", // 8 13 "128-bit complex - DPCOMPLEX" // 9 14 ]; 15 16 action = class 17 Image _result { 18 _vislevel = 3; 19 20 nwidth = Expression "Image width (pixels)" 64; 21 nheight = Expression "Image height (pixels)" 64; 22 nbands = Expression "Image bands" 1; 23 format_option = Option "Image format" format_names 0; 24 type_option = Option_enum "Image type" 25 Image_type.type_names "B_W"; 26 pixel = Expression "Pixel value" 0; 27 28 _result 29 = image_new (to_real nwidth) (to_real nheight) (to_real nbands) 30 (to_real format_option) Image_coding.NOCODING 31 type_option.value_thing pixel.expr 0 0; 32 } 33 } 34 35 Image_new_from_image_item = class 36 Menuaction "_From Image" "make a new image based on image x" { 37 action x = class 38 Image _result { 39 _vislevel = 3; 40 41 pixel = Expression "Pixel value" 0; 42 43 _result 44 = image_new x.width x.height x.bands 45 x.format x.coding x.type pixel.expr x.xoffset x.yoffset; 46 } 47 } 48 49 Image_region_item = class 50 Menupullright "_Region on Image" "make a new region on an image" { 51 Region_item = class 52 Menuaction "_Region" "make a region on an image" { 53 action image = scope.Region_relative image 0.25 0.25 0.5 0.5; 54 } 55 56 Mark_item = class 57 Menuaction "_Point" "make a point on an image" { 58 action image = scope.Mark_relative image 0.5 0.5; 59 } 60 61 Arrow_item = class 62 Menuaction "_Arrow" "make an arrow on an image" { 63 action image = scope.Arrow_relative image 0.25 0.25 0.5 0.5; 64 } 65 66 HGuide_item = class 67 Menuaction "_Horizontal Guide" 68 "make a horizontal guide on an image" { 69 action image = scope.HGuide image 0.5; 70 } 71 72 VGuide_item = class 73 Menuaction "_Vertical Guide" "make a vertical guide on an image" { 74 action image = scope.VGuide image 0.5; 75 } 76 77 sep1 = Menuseparator; 78 79 Move_item = class 80 Menuaction "From Region" 81 "new region on image using existing region as a guide" { 82 action a b 83 = map_binary process a b 84 { 85 process a b 86 = x.Region target x.left x.top x.width x.height, 87 is_Region x 88 = x.Arrow target x.left x.top x.width x.height, 89 is_Arrow x 90 = error "bad arguments to region-from-region" 91 { 92 // prefer image then region 93 compare a b 94 = false, 95 !is_Image a && is_Image b 96 = false, 97 is_Region a && !is_Region b 98 = true; 99 100 [target, x] = sortc compare [a, b]; 101 } 102 } 103 } 104 } 105} 106 107Image_convert_to_image_item = class 108 Menuaction "Con_vert to Image" "convert anything to an image" { 109 action x = to_image x; 110} 111 112Image_number_format_item = class 113 Menupullright "_Format" "convert numeric format" { 114 115 U8_item = class 116 Menuaction "_8 bit unsigned" "convert to unsigned 8 bit [0, 255]" { 117 action x = map_unary cast_unsigned_char x; 118 } 119 120 U16_item = class 121 Menuaction "1_6 bit unsigned" 122 "convert to unsigned 16 bit [0, 65535]" { 123 action x = map_unary cast_unsigned_short x; 124 } 125 126 U32_item = class 127 Menuaction "_32 bit unsigned" 128 "convert to unsigned 32 bit [0, 4294967295]" { 129 action x = map_unary cast_unsigned_int x; 130 } 131 132 sep1 = Menuseparator; 133 134 S8_item = class 135 Menuaction "8 _bit signed" "convert to signed 8 bit [-128, 127]" { 136 action x = map_unary cast_signed_char x; 137 } 138 139 S16_item = class 140 Menuaction "16 b_it signed" 141 "convert to signed 16 bit [-32768, 32767]" { 142 action x = map_unary cast_signed_short x; 143 } 144 145 S32_item = class 146 Menuaction "32 bi_t signed" 147 "convert to signed 32 bit [-2147483648, 2147483647]" { 148 action x = map_unary cast_signed_int x; 149 } 150 151 sep2 = Menuseparator; 152 153 Float_item = class 154 Menuaction "_Single precision float" 155 "convert to IEEE 32 bit float" { 156 action x = map_unary cast_float x; 157 } 158 159 Double_item = class 160 Menuaction "_Double precision float" 161 "convert to IEEE 64 bit float" { 162 action x = map_unary cast_double x; 163 } 164 165 sep3 = Menuseparator; 166 167 Scmplxitem = class 168 Menuaction "Single _precision complex" 169 "convert to 2 x IEEE 32 bit float" { 170 action x = map_unary cast_complex x; 171 } 172 173 Dcmplx_item = class 174 Menuaction "Double p_recision complex" 175 "convert to 2 x IEEE 64 bit float" { 176 action x = map_unary cast_double_complex x; 177 } 178} 179 180Image_header_item = class 181 Menupullright "_Header" "do stuff to the image header" { 182 183 Image_get_item = class 184 Menupullright "_Get" "get header fields" { 185 186 // the header fields we can get 187 fields = class { 188 type = 0; 189 width = 1; 190 height = 2; 191 format = 3; 192 bands = 4; 193 xres = 5; 194 yres = 6; 195 xoffset = 7; 196 yoffset = 8; 197 coding = 9; 198 199 field_names = Enum [ 200 $width => width, 201 $height => height, 202 $bands => bands, 203 $format => format, 204 $type => type, 205 $xres => xres, 206 $yres => yres, 207 $xoffset => xoffset, 208 $yoffset => yoffset, 209 $coding => coding 210 ]; 211 212 field_option name = Option_enum (_ "Field") field_names name; 213 214 field_funcs = Table [ 215 [type, get_type], 216 [width, get_width], 217 [height, get_height], 218 [format, get_format], 219 [bands, get_bands], 220 [xres, get_xres], 221 [yres, get_yres], 222 [xoffset, get_xoffset], 223 [yoffset, get_yoffset], 224 [coding, get_coding] 225 ]; 226 } 227 228 get_field field_name x = class 229 _result { 230 _vislevel = 3; 231 232 field = fields.field_option field_name; 233 234 _result 235 = map_unary (Real @ 236 fields.field_funcs.lookup 0 1 field.value_thing) x; 237 } 238 239 Width_item = class 240 Menuaction "_Width" "get width" { 241 action x = get_field "width" x; 242 } 243 244 Height_item = class 245 Menuaction "_Height" "get height" { 246 action x = get_field "height" x; 247 } 248 249 Bands_item = class 250 Menuaction "_Bands" "get bands" { 251 action x = get_field "bands" x; 252 } 253 254 Format_item = class 255 Menuaction "_Format" "get format" { 256 action x = get_field "format" x; 257 } 258 259 Type_item = class 260 Menuaction "_Type" "get type" { 261 action x = get_field "type" x; 262 } 263 264 Xres_item = class 265 Menuaction "_Xres" "get X resolution" { 266 action x = get_field "xres" x; 267 } 268 269 Yres_item = class 270 Menuaction "_Yres" "get Y resolution" { 271 action x = get_field "yres" x; 272 } 273 274 Xoffset_item = class 275 Menuaction "X_offset" "get X offset" { 276 action x = get_field "xoffset" x; 277 } 278 279 Yoffset_item = class 280 Menuaction "Yo_ffset" "get Y offset" { 281 action x = get_field "yoffset" x; 282 } 283 284 Coding_item = class 285 Menuaction "_Coding" "get coding" { 286 action x = get_field "coding" x; 287 } 288 289 sep1 = Menuseparator; 290 291 Custom_item = class 292 Menuaction "C_ustom" "get any header field" { 293 action x = class 294 _result { 295 _vislevel = 3; 296 297 field = String "Field" "Xsize"; 298 parse = Option "Parse" [ 299 "No parsing", 300 "Parse string as integer", 301 "Parse string as real", 302 "Parse string as hh:mm:ss" 303 ] 0; 304 305 _result 306 = map_unary (wrap @ process @ get_header field.value) x 307 { 308 parse_str parse str = parse (split is_space str)?0; 309 310 parse_field name cast parse x 311 = cast x, is_number x 312 = parse_str parse x, is_string x 313 = error ("not " ++ name); 314 315 get_int = parse_field "int" 316 cast_signed_int parse_int; 317 get_float = parse_field "float" 318 cast_float parse_float; 319 get_time = parse_field "hh:mm:ss" 320 cast_signed_int parse_time; 321 322 wrap x 323 = Real x, is_real x 324 = Vector x, is_real_list x 325 = Image x, is_image x 326 = Bool x, is_bool x 327 = Matrix x, is_matrix x 328 = String "String" x, is_string x 329 = List x, is_list x 330 = x; 331 332 process = [ 333 id, 334 get_int, 335 get_float, 336 get_time 337 ]?parse; 338 } 339 } 340 } 341 } 342 343 sep1 = Menuseparator; 344 345 Image_set_meta_item = class 346 Menuaction "_Set" "set image metadata" { 347 action x = class 348 _result { 349 _vislevel = 3; 350 351 fname = String "Field" "field-name"; 352 val = Expression "Value" 42; 353 354 _result 355 = map_unary process x 356 { 357 process image 358 = set_header fname.value val.expr image; 359 } 360 } 361 } 362 363 Image_edit_header_item = class 364 Menuaction "_Edit" "change advisory header fields of image" { 365 type_names = Image_type.type_names; 366 all_names = sort (map (extract 0) type_names.value); 367 368 get_prop has get def x 369 = get x, has x 370 = def; 371 372 action x = class 373 _result { 374 _vislevel = 3; 375 376 nxres = Expression "Xres" (get_prop has_xres get_xres 1 x); 377 nyres = Expression "Yres" (get_prop has_yres get_yres 1 x); 378 nxoff = Expression "Xoffset" (get_prop has_xoffset get_xoffset 0 x); 379 nyoff = Expression "Yoffset" (get_prop has_yoffset get_yoffset 0 x); 380 381 type_option 382 = Option_enum "Image type" Image_type.type_names 383 (Image_type.type_names.get_name type) 384 { 385 type 386 = x.type, is_Image x 387 = Image_type.MULTIBAND; 388 } 389 390 _result 391 = map_unary process x 392 { 393 process image 394 = Image (im_copy_set image.value type_option.value_thing 395 (to_real nxres) (to_real nyres) 396 (to_real nxoff) (to_real nyoff)); 397 } 398 } 399 } 400} 401 402Image_cache_item = class 403 Menuaction "C_ache" "cache calculated image pixels" { 404 action x = class 405 _result { 406 _vislevel = 3; 407 408 tile_width = Number "Tile width" 128; 409 tile_height = Number "Tile height" 128; 410 max_tiles = Number "Maximum number of tiles to cache" (-1); 411 412 _result 413 = map_unary process x 414 { 415 process image 416 = cache (to_real tile_width) (to_real tile_height) 417 (to_real max_tiles) image; 418 } 419 } 420} 421 422#separator 423 424Image_levels_item = class 425 Menupullright "_Levels" "change image levels" { 426 Scale_item = class 427 Menuaction "_Scale to 0 - 255" "linear transform to fit 0 - 255 range" { 428 action x = map_unary scale x; 429 } 430 431 Linear_item = class 432 Menuaction "_Linear" "linear transform of image levels" { 433 action x = class 434 _result { 435 _vislevel = 3; 436 437 scale = Scale "Scale" 0.001 3 1; 438 offset = Scale "Offset" (-128) 128 0; 439 440 _result 441 = map_unary adj x 442 { 443 adj x 444 // only force back to input type if this is a thing 445 // with a type ... so we work for Colour / Matrix etc. 446 = clip2fmt x.format x', has_member "format" x 447 = x' 448 { 449 x' = x * scale + offset; 450 } 451 } 452 } 453 } 454 455 Gamma_item = class 456 Menuaction "_Power" "power transform of image levels (gamma)" { 457 action x = class 458 _result { 459 _vislevel = 3; 460 461 gamma = Scale "Gamma" 0.001 4 1; 462 image_maximum_hint = "You may need to change image_maximum if " ++ 463 "this is not an 8 bit image"; 464 im_mx 465 = Expression "Image maximum" mx 466 { 467 mx 468 = Image_format.maxval x.format, has_format x 469 = 255; 470 } 471 472 _result 473 = map_unary gam x 474 { 475 gam x 476 = clip2fmt (get_format x) x', has_format x 477 = x' 478 { 479 x' = (im_mx.expr / im_mx.expr ** gamma) * x ** gamma; 480 } 481 } 482 } 483 } 484 485 Tone_item = class 486 Menuaction "_Tone Curve" "adjust tone curve" { 487 action x = class 488 _result { 489 _vislevel = 3; 490 491 b = Scale "Black point" 0 100 0; 492 w = Scale "White point" 0 100 100; 493 494 sp = Scale "Shadow point" 0.1 0.3 0.2; 495 mp = Scale "Mid-tone point" 0.4 0.6 0.5; 496 hp = Scale "Highlight point" 0.7 0.9 0.8; 497 498 sa = Scale "Shadow adjust" (-15) 15 0; 499 ma = Scale "Mid-tone adjust" (-30) 30 0; 500 ha = Scale "Highlight adjust" (-15) 15 0; 501 502 curve = tone_build x.format b w sp mp hp sa ma ha; 503 504 _result = map_unary (hist_map curve) x; 505 } 506 } 507} 508 509Image_transform_item = class 510 Menupullright "_Transform" "transform images" { 511 Rotate_item = class 512 Menupullright "Ro_tate" "rotate image" { 513 Fixed_item = class 514 Menupullright "_Fixed" "clockwise rotation by fixed angles" { 515 rotate_widget default x = class 516 _result { 517 _vislevel = 3; 518 519 angle = Option "Rotate by" [ 520 "Don't rotate", 521 "90 degrees clockwise", 522 "180 degrees", 523 "90 degrees anticlockwise" 524 ] default; 525 526 _result 527 = map_unary process x 528 { 529 process = [ 530 // we can't use id here since we want to "declass" 531 // the members of x ... consider if x is a crop class, 532 // for example, we don't want to inherit from crop, we 533 // want to make a new image class 534 rot180 @ rot180, 535 rot90, 536 rot180, 537 rot270 538 ] ? angle; 539 } 540 } 541 542 Rot90_item = class 543 Menuaction "_90 Degrees" "clockwise rotation by 90 degrees" { 544 action x = rotate_widget 1 x; 545 } 546 547 Rot180_item = class 548 Menuaction "_180 Degrees" "clockwise rotation by 180 degrees" { 549 action x = rotate_widget 2 x; 550 } 551 552 Rot270_item = class 553 Menuaction "_270 Degrees" "clockwise rotation by 270 degrees" { 554 action x = rotate_widget 3 x; 555 } 556 } 557 558 Free_item = class 559 Menuaction "_Free" "clockwise rotation by any angle" { 560 action x = class 561 _result { 562 _vislevel = 3; 563 564 angle = Scale "Angle" (-180) 180 0; 565 interp = Interpolate_picker Interpolate_type.BILINEAR; 566 567 _result 568 = map_unary process x 569 { 570 process image 571 = rotate interp angle image; 572 } 573 } 574 } 575 576 Straighten_item = class 577 Menuaction "_Straighten" 578 ("smallest rotation that makes an arrow either horizontal " ++ 579 "or vertical") { 580 action x = class 581 _result { 582 _vislevel = 3; 583 584 interp = Interpolate_picker Interpolate_type.BILINEAR; 585 586 _result 587 = map_unary straighten x 588 { 589 straighten arrow 590 = rotate interp angle'' arrow.image 591 { 592 x = arrow.width; 593 y = arrow.height; 594 595 angle = im (polar (x, y)); 596 597 angle' 598 = angle - 360, angle > 315 599 = angle - 180, angle > 135 600 = angle; 601 602 angle'' 603 = -angle', angle' >= (-45) && angle' < 45 604 = 90 - angle'; 605 } 606 } 607 } 608 } 609 } 610 611 Flip_item = class 612 Menupullright "_Flip" "mirror left/right or up/down" { 613 Left_right_item = class 614 Menuaction "_Left Right" "mirror object left/right" { 615 action x = map_unary fliplr x; 616 } 617 618 Top_bottom_item = class 619 Menuaction "_Top Bottom" "mirror object top/bottom" { 620 action x = map_unary fliptb x; 621 } 622 } 623 624 Resize_item = class 625 Menupullright "_Resize" "change image size" { 626 Scale_item = class 627 Menuaction "_Scale" "scale image size by a factor" { 628 action x = class 629 _result { 630 _vislevel = 3; 631 632 xfactor = Expression "Horizontal scale factor" 1; 633 yfactor = Expression "Vertical scale factor" 1; 634 interp = Interpolate_picker Interpolate_type.BILINEAR; 635 636 _result 637 = map_unary process x 638 { 639 process image 640 = resize interp xfactor yfactor image; 641 } 642 } 643 } 644 645 Size_item = class 646 Menuaction "_Size To" "resize to a fixed size" { 647 action x = class 648 _result { 649 _vislevel = 3; 650 651 which = Option "Resize axis" [ 652 "Shortest", 653 "Longest", 654 "Horizontal", 655 "Vertical" 656 ] 0; 657 size = Expression "Resize to (pixels)" 128; 658 aspect = Toggle "Break aspect ratio" false; 659 interp = Interpolate_picker Interpolate_type.BILINEAR; 660 661 _result 662 = map_unary process x 663 { 664 process image 665 = resize interp h v image, aspect 666 = resize interp fac fac image 667 { 668 xfac = to_real size / image.width; 669 yfac = to_real size / image.height; 670 max_factor 671 = [xfac, 1], xfac > yfac 672 = [1, yfac]; 673 min_factor 674 = [xfac, 1], xfac < yfac 675 = [1, yfac]; 676 [h, v] = [ 677 max_factor, 678 min_factor, 679 [xfac, 1], 680 [1, yfac]]?which; 681 682 fac 683 = h, v == 1 684 = v; 685 } 686 } 687 } 688 } 689 690 Size_within_item = class 691 Menuaction "Size _Within" "size to fit within a rectangle" { 692 action x = class 693 _result { 694 _vislevel = 3; 695 696 // the rects we size to fit within 697 _rects = [ 698 [2048, 1536], [1920, 1200], [1600, 1200], [1400, 1050], 699 [1280, 1024], [1024, 768], [800, 600], [640, 480] 700 ]; 701 702 within = Option "Fit within (pixels)" ( 703 [print w ++ " x " ++ print h :: [w, h] <- _rects] ++ 704 ["Custom"] 705 ) 4; 706 custom_width = Expression "Custom width" 1000; 707 custom_height = Expression "Custom height" 1000; 708 size = Option "Page size" [ 709 "Full page", "Half page", "Quarter page" 710 ] 0; 711 interp = Interpolate_picker Interpolate_type.BILINEAR; 712 713 _result 714 = map_unary process x 715 { 716 xdiv = [1, 2, 2]?size; 717 ydiv = [1, 1, 2]?size; 718 allrect = _rects ++ [ 719 [custom_width.expr, custom_height.expr] 720 ]; 721 [width, height] = allrect?within; 722 723 process x 724 = resize interp fac fac x, fac < 1 725 = x 726 { 727 xfac = (width / xdiv) / x.width; 728 yfac = (height / ydiv) / x.height; 729 fac = min_pair xfac yfac; 730 } 731 } 732 } 733 } 734 735 Resize_canvas_item = class 736 Menuaction "_Canvas" "change size of surrounding image" { 737 action x = class 738 _result { 739 _vislevel = 3; 740 741 // try to guess a sensible size for the new image 742 _guess_size 743 = x.rect, is_Image x 744 = Rect 0 0 100 100; 745 746 nwidth = Expression "New width (pixels)" _guess_size.width; 747 nheight = Expression "New height (pixels)" _guess_size.height; 748 bgcolour = Expression "Background colour" 0; 749 750 position = Option "Position" [ 751 "North-west", 752 "North", 753 "North-east", 754 "West", 755 "Centre", 756 "East", 757 "South-west", 758 "South", 759 "South-east", 760 "Specify in pixels" 761 ] 4; 762 left = Expression "Pixels from left" 0; 763 top = Expression "Pixels from top" 0; 764 765 _result 766 = map_unary process x 767 { 768 process image 769 = insert_noexpand xp yp image background 770 { 771 width = image.width; 772 height = image.height; 773 coding = image.coding; 774 bands 775 = 3, coding == Image_coding.LABPACK 776 = image.bands; 777 format 778 = Image_format.FLOAT, coding == Image_coding.LABPACK 779 = image.format; 780 type = image.type; 781 782 // placement vectors ... left, centre, right 783 xposv = [0, to_real nwidth / 2 - width / 2, 784 to_real nwidth - width]; 785 yposv = [0, to_real nheight / 2 - height / 2, 786 to_real nheight - height]; 787 xp 788 = left, position == 9 789 = xposv?((int) (position % 3)); 790 yp 791 = top, position == 9 792 = yposv?((int) (position / 3)); 793 794 background = image_new nwidth nheight 795 bands format coding type bgcolour.expr 0 0; 796 } 797 } 798 } 799 } 800 } 801 802 Image_perspective_item = Perspective_item; 803 804 Image_rubber_item = class 805 Menupullright "Ru_bber Sheet" 806 "automatically warp images to superposition" { 807 rubber_interp = Option "Interpolation" ["Nearest", "Bilinear"] 1; 808 rubber_order = Option "Order" ["0", "1", "2", "3"] 1; 809 rubber_wrap = Toggle "Wrap image edges" false; 810 811 // a transform ... a matrix, plus the size of the image the 812 // matrix was made for 813 Transform matrix image_width image_height = class 814 matrix { 815 // scale a transform ... if it worked for a m by n image, make 816 // it work for a (m * xfac) by (y * yfac) image 817 rescale xfac yfac 818 = Transform (Matrix (map2 (map2 multiply) matrix.value facs)) 819 (image_width * xfac) (image_height * yfac) 820 { 821 facs = [ 822 [xfac, yfac], 823 [1, 1], 824 [1, 1], 825 [1 / xfac, 1 / yfac], 826 [1 / xfac, 1 / yfac], 827 [1 / xfac, 1 / yfac] 828 ]; 829 } 830 } 831 832 // yuk!!!! fix is_instanceof to not need absolute names 833 is_Transform = is_instanceof 834 "Image_transform_item.Image_rubber_item.Transform"; 835 836 Find_item = class 837 Menuaction "_Find" 838 ("find a transform which will map sample image onto " ++ 839 "reference") { 840 action reference sample = class 841 _trn { 842 _vislevel = 3; 843 844 // controls 845 order = rubber_order; 846 interp = rubber_interp; 847 wrap = rubber_wrap; 848 max_err = Expression "Maximum error" 0.3; 849 max_iter = Expression "Maximum iterations" 10; 850 851 // transform 852 [sample', trn, err] = transform_search 853 max_err max_iter order interp wrap 854 sample reference; 855 transformed_image = Image sample'; 856 _trn = Transform trn reference.width reference.height; 857 final_error = err; 858 } 859 } 860 861 Apply_item = class 862 Menuaction "_Apply" "apply a transform to an image" { 863 action a b = class 864 _result { 865 _vislevel = 3; 866 867 // controls 868 interp = rubber_interp; 869 wrap = rubber_wrap; 870 871 _result 872 = map_binary trans a b 873 { 874 trans a b 875 = transform interp wrap t' i 876 { 877 // get the transform arg first 878 [i, t] = sortc (const is_Transform) [a, b]; 879 t' = t.rescale (i.width / t.image_width) 880 (i.height / t.image_height); 881 } 882 } 883 } 884 } 885 } 886 887 sep1 = Menuseparator; 888 889 Match_item = class 890 Menuaction "_Linear Match" 891 "rotate and scale one image to match another" { 892 action x y = class 893 _result { 894 _vislevel = 3; 895 896 // try to find an image ... for a group, get the first item 897 find_image x 898 = x, is_Image x 899 = find_image x?0, is_list x 900 = find_image x.value, is_class x && has_value x 901 = error "unable to find image"; 902 903 _a = find_image x; 904 _b = find_image y; 905 906 ap1 = Mark_relative _a 0.5 0.25; 907 bp1 = Mark_relative _b 0.5 0.25; 908 ap2 = Mark_relative _a 0.5 0.75; 909 bp2 = Mark_relative _b 0.5 0.75; 910 911 refine = Toggle "Refine selected tie-points" false; 912 lock = Toggle "No resize" false; 913 914 _result 915 = map_binary process x y 916 { 917 process a b 918 = Image b''' 919 { 920 _prefs = Workspaces.Preferences; 921 window = _prefs.MOSAIC_WINDOW_SIZE; 922 object = _prefs.MOSAIC_OBJECT_SIZE; 923 924 a' = a.value; 925 b' = b.value; 926 927 b'' = clip2fmt a.format b'; 928 929 // return p2 ... if lock is set, return a p2 a standard 930 // distance along the vector joining p1 and p2 931 norm p1 p2 932 = Rect left' top' 0 0, lock 933 = p2 934 { 935 v = (p2.left - p1.left, p2.top - p1.top); 936 // 100000 to give precision since we pass points as 937 // ints to match 938 n = 100000 * sign v; 939 left' = p1.left + re n; 940 top' = p1.top + im n; 941 } 942 943 ap2'' = norm ap1 ap2; 944 bp2'' = norm bp1 bp2; 945 946 b''' 947 = im_match_linear_search a' b'' 948 ap1.left ap1.top bp1.left bp1.top 949 ap2''.left ap2''.top bp2''.left bp2''.top 950 object window, 951 // we can't search if lock is on 952 refine && !lock 953 = im_match_linear a' b'' 954 ap1.left ap1.top bp1.left bp1.top 955 ap2''.left ap2''.top bp2''.left bp2''.top; 956 } 957 } 958 } 959 } 960 961 Image_perspective_match_item = Perspective_match_item; 962} 963 964Image_band_item = class 965 Menupullright "_Band" "manipulate image bands" { 966 // like extract_bands, but return [] for zero band image 967 // makes compose a bit simpler 968 exb b n x 969 = [], to_real n == 0 970 = extract_bands b n x; 971 972 Extract_item = class Menuaction "_Extract" "extract bands from image" { 973 action x = class 974 _result { 975 _vislevel = 3; 976 977 first = Expression "Extract from band" 0; 978 number = Expression "Extract this many bands" 1; 979 980 _result = map_unary (exb first number) x; 981 } 982 } 983 984 Insert_item = class Menuaction "_Insert" "insert bands into image" { 985 action x y = class 986 _result { 987 _vislevel = 3; 988 989 first = Expression "Insert at position" 0; 990 991 _result 992 = map_binary process x y 993 { 994 process im1 im2 995 = exb 0 f im1 ++ im2 ++ exb f (b - f) im1 996 { 997 f = to_real first; 998 b = im1.bands; 999 } 1000 } 1001 } 1002 } 1003 1004 Delete_item = class Menuaction "_Delete" "delete bands from image" { 1005 action x = class 1006 _result { 1007 _vislevel = 3; 1008 1009 first = Expression "Delete from band" 0; 1010 number = Expression "Delete this many bands" 1; 1011 1012 _result 1013 = map_unary process x 1014 { 1015 process im 1016 = exb 0 f im ++ exb (f + n) (b - (f + n)) im 1017 { 1018 f = to_real first; 1019 n = to_real number; 1020 b = im.bands; 1021 } 1022 } 1023 } 1024 } 1025 1026 Bandwise_item = Image_join_item.Bandwise_item; 1027 1028 sep1 = Menuseparator; 1029 1030 Bandand_item = class 1031 Menuaction "Bitwise Band AND" "bitwise AND of image bands" { 1032 action x = bandand x; 1033 } 1034 1035 Bandor_item = class 1036 Menuaction "Bitwise Band OR" "bitwise OR of image bands" { 1037 action x = bandor x; 1038 } 1039 1040 sep2 = Menuseparator; 1041 1042 To_dimension_item = class 1043 Menuaction "To D_imension" "convert bands to width or height" { 1044 action x = class 1045 _result { 1046 _vislevel = 3; 1047 1048 orientation = Option "Orientation" [ 1049 "Horizontal", 1050 "Vertical" 1051 ] 0; 1052 1053 _result 1054 = map_unary process x 1055 { 1056 process im 1057 = foldl1 [join_lr, join_tb]?orientation (bandsplit im); 1058 } 1059 } 1060 } 1061 1062 To_bands_item = class 1063 Menuaction "To B_ands" "turn width or height to bands" { 1064 action x = class 1065 _result { 1066 _vislevel = 3; 1067 1068 orientation = Option "Orientation" [ 1069 "Horizontal", 1070 "Vertical" 1071 ] 0; 1072 1073 _result 1074 = map_unary process x 1075 { 1076 process im 1077 = bandjoin (map extract_column [0 .. im.width - 1]), 1078 orientation == 0 1079 = bandjoin (map extract_row [0 .. im.height - 1]) 1080 { 1081 extract_column n 1082 = extract_area n 0 1 im.height im; 1083 extract_row n 1084 = extract_area 0 n im.width 1 im; 1085 } 1086 } 1087 } 1088 } 1089} 1090 1091Image_crop_item = class 1092 Menuaction "_Crop" "extract a rectangular area from an image" { 1093 action x 1094 = crop x [l, t, w, h] 1095 { 1096 fields = [ 1097 [has_left, get_left, 0], 1098 [has_top, get_top, 0], 1099 [has_width, get_width, 100], 1100 [has_height, get_height, 100] 1101 ]; 1102 1103 [l, t, w, h] 1104 = map get_default fields 1105 { 1106 get_default line 1107 = get x, has x 1108 = default 1109 { 1110 [has, get, default] = line; 1111 } 1112 } 1113 } 1114 1115 crop x geo = class 1116 _result { 1117 _vislevel = 3; 1118 1119 l = Expression "Crop left" ((int) (geo?0 + geo?2 / 4)); 1120 t = Expression "Crop top" ((int) (geo?1 + geo?3 / 4)); 1121 w = Expression "Crop width" (max_pair 1 ((int) (geo?2 / 2))); 1122 h = Expression "Crop height" (max_pair 1 ((int) (geo?3 / 2))); 1123 1124 _result 1125 = map_nary (list_5ary extract) [x, l.expr, t.expr, w.expr, h.expr] 1126 { 1127 extract im l t w h 1128 = extract_area left' top' width' height' im 1129 { 1130 width' = min_pair (to_real w) im.width; 1131 height' = min_pair (to_real h) im.height; 1132 left' = range 0 (to_real l) (im.width - width'); 1133 top' = range 0 (to_real t) (im.height - height'); 1134 } 1135 } 1136 } 1137} 1138 1139Image_insert_item = class 1140 Menuaction "_Insert" "insert a small image into a large image" { 1141 action a b 1142 = insert_position, is_Group a || is_Group b 1143 = insert_area 1144 { 1145 insert_area = class 1146 _result { 1147 _check_args = [ 1148 [a, "a", check_Image], 1149 [b, "b", check_Image] 1150 ]; 1151 _vislevel = 3; 1152 1153 // sort to get smallest first 1154 _pred x y = x.width * x.height < y.width * y.height; 1155 [_a', _b'] = sortc _pred [a, b]; 1156 1157 place 1158 = Area _b' left top width height 1159 { 1160 // be careful in case b is smaller than a 1161 left = max_pair 0 ((_b'.width - _a'.width) / 2); 1162 top = max_pair 0 ((_b'.height - _a'.height) / 2); 1163 width = min_pair _a'.width _b'.width; 1164 height = min_pair _a'.height _b'.height; 1165 } 1166 1167 _result 1168 = insert_noexpand place.left place.top 1169 (clip2fmt _b'.format a'') _b' 1170 { 1171 a'' = extract_area 0 0 place.width place.height _a'; 1172 } 1173 } 1174 1175 insert_position = class 1176 _result { 1177 _vislevel = 3; 1178 1179 position = Option "Position" [ 1180 "North-west", 1181 "North", 1182 "North-east", 1183 "West", 1184 "Centre", 1185 "East", 1186 "South-west", 1187 "South", 1188 "South-east", 1189 "Specify in pixels" 1190 ] 4; 1191 left = Expression "Pixels from left" 0; 1192 top = Expression "Pixels from top" 0; 1193 1194 _result 1195 = map_binary insert a b 1196 { 1197 insert a b 1198 = insert_noexpand left top (clip2fmt b.format a) b, 1199 position == 9 1200 = insert_noexpand xp yp (clip2fmt b.format a) b 1201 { 1202 xr = b.width - a.width; 1203 yr = b.height - a.height; 1204 xp = [0, xr / 2, xr]?((int) (position % 3)); 1205 yp = [0, yr / 2, yr]?((int) (position / 3)); 1206 } 1207 } 1208 } 1209 } 1210} 1211 1212Image_select_item = Select_item; 1213 1214Image_draw_item = class 1215 Menupullright "_Draw" "draw lines, circles, rectangles, floods" { 1216 Line_item = class Menuaction "_Line" "draw line on image" { 1217 action x = class 1218 _result { 1219 _vislevel = 3; 1220 1221 x1 = Expression "Start x" 0; 1222 y1 = Expression "Start y" 0; 1223 x2 = Expression "End x" 100; 1224 y2 = Expression "End y" 100; 1225 1226 i = Expression "Ink" [0]; 1227 1228 _result 1229 = map_unary line x 1230 { 1231 line im 1232 = draw_line x1 y1 x2 y2 i.expr im; 1233 } 1234 } 1235 } 1236 1237 Rect_item = class Menuaction "_Rectangle" "draw rectangle on image" { 1238 action x = class 1239 _result { 1240 _vislevel = 3; 1241 1242 rx = Expression "Left" 50; 1243 ry = Expression "Top" 50; 1244 rw = Expression "Width" 100; 1245 rh = Expression "Height" 100; 1246 1247 f = Toggle "Fill" true; 1248 1249 t = Scale "Line thickness" 1 50 3; 1250 1251 i = Expression "Ink" [0]; 1252 1253 _result 1254 = map_unary rect x 1255 { 1256 rect im 1257 = draw_rect_width rx ry rw rh f t i.expr im; 1258 } 1259 } 1260 } 1261 1262 Circle_item = class Menuaction "_Circle" "draw circle on image" { 1263 action x = class 1264 _result { 1265 _vislevel = 3; 1266 1267 cx = Expression "Centre x" 100; 1268 cy = Expression "Centre y" 100; 1269 r = Expression "Radius" 50; 1270 1271 f = Toggle "Fill" true; 1272 1273 i = Expression "Ink" [0]; 1274 1275 _result 1276 = map_unary circle x 1277 { 1278 circle im 1279 = draw_circle cx cy r f i.expr im; 1280 } 1281 } 1282 } 1283 1284 Flood_item = class Menuaction "_Flood" "flood bounded area of image" { 1285 action x = class 1286 _result { 1287 _vislevel = 3; 1288 1289 sx = Expression "Start x" 100; 1290 sy = Expression "Start y" 100; 1291 1292 e = Option "Flood while" [ 1293 "Not equal to ink", 1294 "Equal to start point" 1295 ] 0; 1296 1297 // pick a default ink that won't flood, if we can 1298 i 1299 = Expression "Ink" default_ink 1300 { 1301 default_ink 1302 = [0], ! has_image x 1303 = pixel; 1304 pixel = map mean (bandsplit (extract_area sx sy 1 1 im)); 1305 im = get_image x; 1306 } 1307 1308 _result 1309 = map_unary flood x 1310 { 1311 flood im 1312 = draw_flood sx sy i.expr im, e == 0 1313 = draw_flood_blob sx sy i.expr im; 1314 } 1315 } 1316 } 1317 1318 Draw_scalebar_item = class Menuaction "_Scale" "draw scale bar" { 1319 action x = class 1320 _result { 1321 _vislevel = 3; 1322 1323 px = Expression "Left" 50; 1324 py = Expression "Top" 50; 1325 wid = Expression "Width" 100; 1326 thick = Scale "Line thickness" 1 50 3; 1327 text = String "Dimension text" "50μm"; 1328 font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; 1329 pos = Option "Position Text" ["Above", "Below"] 1; 1330 vp = Option "Dimension by" [ 1331 "Inner Vertical Edge", 1332 "Centre of Vertical", 1333 "Outer Vertical Edge" 1334 ] 1; 1335 dpi = Expression "DPI" 100; 1336 ink = Colour "Lab" [50,0,0]; 1337 1338 _result 1339 = map_unary process x 1340 { 1341 process im 1342 = blend (Image scale) ink' im 1343 { 1344 // make an ink compatible with the image 1345 ink' = colour_transform_to (get_type im) ink; 1346 1347 x = to_real px; 1348 y = to_real py; 1349 w = to_real wid; 1350 d = to_real dpi; 1351 1352 t = floor thick; 1353 1354 bg = image_new (get_width im) (get_height im) (get_bands im) 1355 (get_format im) (get_coding im) (get_type im) 0 0 0; 1356 draw_block x y w t im = 1357 draw_rect_width x y w t true 1 [255] im; 1358 label = im_text text.value font.value w 1 d; 1359 lw = get_width label; 1360 lh = get_height label; 1361 ly = [y - lh - t, y + 2 * t]?pos; 1362 vx = [ 1363 [x - t, x + w], 1364 [x - t / 2, x + w - t / 2], 1365 [x, x + w - t] 1366 ]?vp; 1367 1368 scale = (draw_block x y w t @ 1369 draw_block vx?0 (y - 2 * t) t (t * 5) @ 1370 draw_block vx?1 (y - 2 * t) t (t * 5) @ 1371 insert_noexpand (x + w / 2 - lw / 2) ly label) 1372 bg; 1373 } 1374 } 1375 } 1376 } 1377} 1378 1379Image_join_item = class 1380 Menupullright "_Join" "join two or more images together" { 1381 Bandwise_item = class 1382 Menuaction "_Bandwise Join" "join two images bandwise" { 1383 action a b = join a b; 1384 } 1385 1386 sep1 = Menuseparator; 1387 1388 join_lr shim bg align a b 1389 = im2 1390 { 1391 w = a.width + b.width + shim; 1392 h = max_pair a.height b.height; 1393 1394 back = image_new w h a.bands a.format a.coding a.type bg 0 0; 1395 1396 ya = [0, max_pair 0 ((b.height - a.height)/2), 1397 max_pair 0 (b.height - a.height)]; 1398 yb = [0, max_pair 0 ((a.height - b.height)/2), 1399 max_pair 0 (a.height - b.height)]; 1400 1401 im1 = insert_noexpand 0 ya?align a back; 1402 im2 = insert_noexpand (a.width + shim) yb?align b im1; 1403 } 1404 1405 join_tb shim bg align a b 1406 = im2 1407 { 1408 w = max_pair a.width b.width; 1409 h = a.height + b.height + shim; 1410 1411 back = image_new w h a.bands a.format a.coding a.type bg 0 0; 1412 1413 xa = [0, max_pair 0 ((b.width - a.width)/2), 1414 max_pair 0 (b.width - a.width)]; 1415 xb = [0, max_pair 0 ((a.width - b.width)/2), 1416 max_pair 0 (a.width - b.width)]; 1417 1418 im1 = insert_noexpand xa?align 0 a back; 1419 im2 = insert_noexpand xb?align (a.height + shim) b im1; 1420 } 1421 1422 halign_names = ["Top", "Centre", "Bottom"]; 1423 valign_names = ["Left", "Centre", "Right"]; 1424 1425 Left_right_item = class 1426 Menuaction "_Left to Right" "join two images left-right" { 1427 action a b = class 1428 _result { 1429 _vislevel = 3; 1430 1431 shim = Scale "Spacing" 0 100 0; 1432 bg_colour = Expression "Background colour" 0; 1433 align = Option "Alignment" halign_names 1; 1434 1435 _result = map_binary 1436 (join_lr shim.value bg_colour.expr align.value) a b; 1437 } 1438 } 1439 1440 Top_bottom_item = class 1441 Menuaction "_Top to Bottom" "join two images top-bottom" { 1442 action a b = class 1443 _result { 1444 _vislevel = 3; 1445 1446 shim = Scale "Spacing" 0 100 0; 1447 bg_colour = Expression "Background colour" 0; 1448 align = Option "Alignment" valign_names 1; 1449 1450 _result = map_binary 1451 (join_tb shim.value bg_colour.expr align.value) a b; 1452 } 1453 } 1454 1455 sep2 = Menuseparator; 1456 1457 Array_item = class 1458 Menuaction "_Array" 1459 "join a list of lists of images into a single image" { 1460 action x = class 1461 _result { 1462 _vislevel = 3; 1463 1464 hshim = Scale "Horizontal spacing" (-100) (100) 0; 1465 vshim = Scale "Vertical spacing" (-100) (100) 0; 1466 bg_colour = Expression "Background colour" 0; 1467 halign = Option "Horizontal alignment" valign_names 1; 1468 valign = Option "Vertical alignment" halign_names 1; 1469 1470 // we can't use map_unary since chop-into-tiles returns a group of 1471 // groups and we want to be able to reassemble that 1472 // TODO: chop-into-tiles should return an array class which 1473 // displays as group but does not have the looping behaviour? 1474 _result 1475 = (image_set_origin 0 0 @ 1476 foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ 1477 map (foldl1 (join_lr hshim.value 1478 bg_colour.expr valign.value))) (to_list (to_list x)); 1479 } 1480 } 1481 1482 ArrayFL_item = class 1483 Menuaction "_Array from List" 1484 "join a list of images into a single image" { 1485 action x = class 1486 _result { 1487 _vislevel = 3; 1488 1489 ncol = Number "Max. Number of Columns" 1; 1490 hshim = Scale "Horizontal spacing" (-100) (100) 0; 1491 vshim = Scale "Vertical spacing" (-100) (100) 0; 1492 bg_colour = Expression "Background colour" 0; 1493 halign = Option "Horizontal alignment" valign_names 1; 1494 valign = Option "Vertical alignment" halign_names 1; 1495 1496 _l 1497 = split_lines ncol.value x.value, is_Group x 1498 = split_lines ncol.value x; 1499 1500 _result 1501 = (image_set_origin 0 0 @ 1502 foldl1 (join_tb vshim.value bg_colour.expr halign.value) @ 1503 map (foldl1 (join_lr hshim.value 1504 bg_colour.expr valign.value))) (to_list (to_list _l)); 1505 } 1506 } 1507} 1508 1509Image_tile_item = class 1510 Menupullright "Til_e" "tile an image across and down" { 1511 tile_widget default_type x = class 1512 _result { 1513 _vislevel = 3; 1514 1515 across = Expression "Tiles across" 2; 1516 down = Expression "Tiles down" 2; 1517 repeat = Option "Tile type" 1518 ["Replicate", "Four-way mirror"] default_type; 1519 1520 _result 1521 = map_unary process x 1522 { 1523 process image 1524 = tile across down image, repeat == 0 1525 = tile across down image'' 1526 { 1527 image' = insert image.width 0 (fliplr image) image; 1528 image'' = insert 0 image.height (fliptb image') image'; 1529 } 1530 } 1531 } 1532 1533 Replicate_item = class 1534 Menuaction "_Replicate" "replicate image across and down" { 1535 action x = tile_widget 0 x; 1536 } 1537 1538 Fourway_item = class 1539 Menuaction "_Four-way Mirror" "four-way mirror across and down" { 1540 action x = tile_widget 1 x; 1541 } 1542 1543 Chop_item = class 1544 Menuaction "_Chop Into Tiles" "slice an image into tiles" { 1545 action x = class 1546 _result { 1547 _vislevel = 3; 1548 1549 tile_width = Expression "Tile width" 100; 1550 tile_height = Expression "Tile height" 100; 1551 hoverlap = Expression "Horizontal overlap" 0; 1552 voverlap = Expression "Vertical overlap" 0; 1553 1554 _result 1555 = map_unary (Group @ map Group @ process) x 1556 { 1557 process x 1558 = imagearray_chop tile_width tile_height 1559 hoverlap voverlap x; 1560 } 1561 } 1562 } 1563} 1564 1565#separator 1566 1567Pattern_images_item = class 1568 Menupullright "_Patterns" "make a variety of useful patterns" { 1569 Grey_item = class 1570 Menuaction "Grey _Ramp" "make a smooth grey ramp" { 1571 action = class 1572 _result { 1573 _vislevel = 3; 1574 1575 nwidth = Expression "Image width (pixels)" 64; 1576 nheight = Expression "Image height (pixels)" 64; 1577 orientation = Option "Orientation" [ 1578 "Horizontal", 1579 "Vertical" 1580 ] 0; 1581 foption = Option "Format" ["8 bit", "float"] 0; 1582 1583 _result 1584 = Image im 1585 { 1586 gen 1587 = im_grey, foption == 0 1588 = im_fgrey; 1589 w = to_real nwidth; 1590 h = to_real nheight; 1591 im 1592 = gen w h, orientation == 0 1593 = rot90 (gen h w); 1594 } 1595 } 1596 } 1597 1598 Xy_item = class 1599 Menuaction "_XY Image" 1600 "make a two band image whose pixel values are their coordinates" { 1601 action = class 1602 _result { 1603 _vislevel = 3; 1604 1605 nwidth = Expression "Image width (pixels)" 64; 1606 nheight = Expression "Image height (pixels)" 64; 1607 1608 _result = Image (make_xy nwidth nheight); 1609 } 1610 } 1611 1612 Gaussian_item = class 1613 Menuaction "Gaussian _Noise" "make an image of gaussian noise" { 1614 action = class 1615 _result { 1616 _vislevel = 3; 1617 1618 nwidth = Expression "Image width (pixels)" 64; 1619 nheight = Expression "Image height (pixels)" 64; 1620 mean = Scale "Mean" 0 255 128; 1621 deviation = Scale "Deviation" 0 128 50; 1622 1623 _result = Image (im_gaussnoise (to_real nwidth) (to_real nheight) 1624 mean.value deviation.value); 1625 } 1626 } 1627 1628 Fractal_item = class 1629 Menuaction "_Fractal" "make a fractal image" { 1630 action = class 1631 _result { 1632 _vislevel = 3; 1633 1634 nsize = Expression "Image size (pixels)" 64; 1635 dimension = Scale "Dimension" 2.001 2.999 2.001; 1636 1637 _result = Image (im_fractsurf (to_real nsize) dimension.value); 1638 } 1639 } 1640 1641 Checkerboard_item = class 1642 Menuaction "_Checkerboard" "make a checkerboard image" { 1643 action = class 1644 _result { 1645 _vislevel = 3; 1646 1647 nwidth = Expression "Image width (pixels)" 64; 1648 nheight = Expression "Image height (pixels)" 64; 1649 hpsize = Expression "Horizontal patch size" 8; 1650 vpsize = Expression "Vertical patch size" 8; 1651 hpoffset = Expression "Horizontal patch offset" 0; 1652 vpoffset = Expression "Vertical patch offset" 0; 1653 1654 _result 1655 = Image (xstripes ^ ystripes) 1656 { 1657 pixels = make_xy nwidth nheight; 1658 xpixels = pixels?0 + to_real hpoffset; 1659 ypixels = pixels?1 + to_real vpoffset; 1660 1661 make_stripe pix swidth = pix % (swidth * 2) >= swidth; 1662 1663 xstripes = make_stripe xpixels (to_real hpsize); 1664 ystripes = make_stripe ypixels (to_real vpsize); 1665 } 1666 } 1667 } 1668 1669 Grid_item = class 1670 Menuaction "Gri_d" "make a grid" { 1671 action = class 1672 _result { 1673 _vislevel = 3; 1674 1675 nwidth = Expression "Image width (pixels)" 64; 1676 nheight = Expression "Image height (pixels)" 64; 1677 hspace = Expression "Horizontal line spacing" 8; 1678 vspace = Expression "Vertical line spacing" 8; 1679 thick = Expression "Line thickness" 1; 1680 hoff = Expression "Horizontal grid offset" 4; 1681 voff = Expression "Vertical grid offset" 4; 1682 1683 _result 1684 = Image (xstripes | ystripes) 1685 { 1686 pixels = make_xy nwidth nheight; 1687 xpixels = pixels?0 + to_real hoff; 1688 ypixels = pixels?1 + to_real voff; 1689 1690 make_stripe pix swidth = pix % swidth < to_real thick; 1691 1692 xstripes = make_stripe xpixels (to_real hspace); 1693 ystripes = make_stripe ypixels (to_real vspace); 1694 } 1695 } 1696 } 1697 1698 Text_item = class 1699 Menuaction "_Text" "make a bitmap of some text" { 1700 action = class 1701 _result { 1702 _vislevel = 3; 1703 1704 text = String "Text to paint" "<i>Hello</i> world!"; 1705 font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; 1706 wrap = Expression "Wrap text at" 500; 1707 align = Option "Alignment" [ 1708 "Left", 1709 "Centre", 1710 "Right" 1711 ] 0; 1712 dpi = Expression "DPI" 300; 1713 1714 _result = Image (im_text text.value font.value 1715 (to_real wrap) align.value (to_real dpi)); 1716 } 1717 } 1718 1719 New_CIELAB_slice_item = class 1720 Menuaction "CIELAB _Slice" "make a slice through CIELAB space" { 1721 action = class 1722 _result { 1723 _vislevel = 3; 1724 1725 nsize = Expression "Image size (pixels)" 64; 1726 L = Scale "L value" 0 100 50; 1727 1728 _result = Image (lab_slice (to_real nsize) L.value); 1729 } 1730 } 1731 1732 sense_option = Option "Sense" [ 1733 "Pass", 1734 "Reject" 1735 ] 0; 1736 1737 build fn size 1738 = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) 1739 (im_create_fmask size size); 1740 1741 New_ideal_item = class 1742 Menupullright "_Ideal Fourier Mask" 1743 "make various ideal Fourier filter masks" { 1744 High_low_item = class 1745 Menuaction "_High or Low Pass" 1746 ("make a mask image for a highpass/lowpass " ++ 1747 "ideal Fourier filter") { 1748 action = class 1749 _result { 1750 _vislevel = 3; 1751 1752 nsize = Expression "Image size (pixels)" 64; 1753 sense = sense_option; 1754 fc = Scale "Frequency cutoff" 0.01 0.99 0.5; 1755 1756 _result 1757 = build param (to_real nsize) 1758 { 1759 param f = f sense.value fc.value 0 0 0 0; 1760 } 1761 } 1762 } 1763 1764 Ring_item = class 1765 Menuaction "_Ring Pass or Ring Reject" 1766 ("make a mask image for an ring pass/reject " ++ 1767 "ideal Fourier filter") { 1768 action = class 1769 _result { 1770 _vislevel = 3; 1771 1772 nsize = Expression "Image size (pixels)" 64; 1773 sense = sense_option; 1774 fc = Scale "Frequency cutoff" 0.01 0.99 0.5; 1775 rw = Scale "Ring width" 0.01 0.99 0.5; 1776 1777 _result 1778 = build param (to_real nsize) 1779 { 1780 param f = f (sense.value + 6) fc.value rw.value 0 0 0; 1781 } 1782 } 1783 } 1784 1785 Band_item = class 1786 Menuaction "_Band Pass or Band Reject" 1787 ("make a mask image for a band pass/reject " ++ 1788 "ideal Fourier filter") { 1789 action = class 1790 _result { 1791 _vislevel = 3; 1792 1793 nsize = Expression "Image size (pixels)" 64; 1794 sense = sense_option; 1795 fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; 1796 fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; 1797 r = Scale "Radius" 0.01 0.99 0.5; 1798 1799 _result 1800 = build param (to_real nsize) 1801 { 1802 param f = f (sense.value + 12) fcx.value fcy.value 1803 r.value 0 0; 1804 } 1805 } 1806 } 1807 } 1808 1809 New_gaussian_item = class 1810 Menupullright "_Gaussian Fourier Mask" 1811 "make various Gaussian Fourier filter masks" { 1812 High_low_item = class 1813 Menuaction "_High or Low Pass" 1814 ("make a mask image for a highpass/lowpass " ++ 1815 "Gaussian Fourier filter") { 1816 action = class 1817 _result { 1818 _vislevel = 3; 1819 1820 nsize = Expression "Image size (pixels)" 64; 1821 sense = sense_option; 1822 fc = Scale "Frequency cutoff" 0.01 0.99 0.5; 1823 ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; 1824 1825 _result 1826 = build param (to_real nsize) 1827 { 1828 param f = f (sense.value + 4) fc.value ac.value 0 0 0; 1829 } 1830 } 1831 } 1832 1833 Ring_item = class 1834 Menuaction "_Ring Pass or Ring Reject" 1835 ("make a mask image for an ring pass/reject " ++ 1836 "Gaussian Fourier filter") { 1837 action = class 1838 _result { 1839 _vislevel = 3; 1840 1841 nsize = Expression "Image size (pixels)" 64; 1842 sense = sense_option; 1843 fc = Scale "Frequency cutoff" 0.01 0.99 0.5; 1844 ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; 1845 rw = Scale "Ring width" 0.01 0.99 0.5; 1846 1847 _result 1848 = build param (to_real nsize) 1849 { 1850 param f = f (sense.value + 10) fc.value rw.value 1851 ac.value 0 0; 1852 } 1853 } 1854 } 1855 1856 Band_item = class 1857 Menuaction "_Band Pass or Band Reject" 1858 ("make a mask image for a band pass/reject " ++ 1859 "Gaussian Fourier filter") { 1860 action = class 1861 _result { 1862 _vislevel = 3; 1863 1864 nsize = Expression "Image size (pixels)" 64; 1865 sense = sense_option; 1866 fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; 1867 fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; 1868 r = Scale "Radius" 0.01 0.99 0.5; 1869 ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; 1870 1871 _result 1872 = build param (to_real nsize) 1873 { 1874 param f = f (sense.value + 16) fcx.value fcy.value 1875 r.value ac.value 0; 1876 } 1877 } 1878 } 1879 } 1880 1881 New_butterworth_item = class 1882 Menupullright "_Butterworth Fourier Mask" 1883 "make various Butterworth Fourier filter masks" { 1884 High_low_item = class 1885 Menuaction "_High or Low Pass" 1886 ("make a mask image for a highpass/lowpass " ++ 1887 "Butterworth Fourier filter") { 1888 action = class 1889 _result { 1890 _vislevel = 3; 1891 1892 nsize = Expression "Image size (pixels)" 64; 1893 sense = sense_option; 1894 fc = Scale "Frequency cutoff" 0.01 0.99 0.5; 1895 ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; 1896 order = Scale "Order" 1 10 2; 1897 1898 _result 1899 = build param (to_real nsize) 1900 { 1901 param f = f (sense.value + 2) order.value fc.value 1902 ac.value 0 0; 1903 } 1904 } 1905 } 1906 1907 Ring_item = class 1908 Menuaction "_Ring Pass or Ring Reject" 1909 ("make a mask image for an ring pass/reject " ++ 1910 "Butterworth Fourier filter") { 1911 action = class 1912 _result { 1913 _vislevel = 3; 1914 1915 nsize = Expression "Image size (pixels)" 64; 1916 sense = sense_option; 1917 fc = Scale "Frequency cutoff" 0.01 0.99 0.5; 1918 ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; 1919 rw = Scale "Ring width" 0.01 0.99 0.5; 1920 order = Scale "Order" 1 10 2; 1921 1922 _result 1923 = build param (to_real nsize) 1924 { 1925 param f = f (sense.value + 8) order.value fc.value 1926 rw.value ac.value 0; 1927 } 1928 } 1929 } 1930 1931 Band_item = class 1932 Menuaction "_Band Pass or Band Reject" 1933 ("make a mask image for a band pass/reject " ++ 1934 "Butterworth Fourier filter") { 1935 action = class 1936 _result { 1937 _vislevel = 3; 1938 1939 nsize = Expression "Image size (pixels)" 64; 1940 sense = sense_option; 1941 fcx = Scale "Horizontal frequency cutoff" 0.01 0.99 0.5; 1942 fcy = Scale "Vertical frequency cutoff" 0.01 0.99 0.5; 1943 r = Scale "Radius" 0.01 0.99 0.5; 1944 ac = Scale "Amplitude cutoff" 0.01 0.99 0.5; 1945 order = Scale "Order" 1 10 2; 1946 1947 _result 1948 = build param (to_real nsize) 1949 { 1950 param f = f (sense.value + 14) order.value fcx.value 1951 fcy.value r.value ac.value; 1952 } 1953 } 1954 } 1955 } 1956} 1957 1958Test_images_item = class 1959 Menupullright "Test I_mages" "make a variety of test images" { 1960 Eye_item = class 1961 Menuaction "_Spatial Response" 1962 "image for testing the eye's spatial response" { 1963 action = class 1964 _result { 1965 _vislevel = 3; 1966 1967 nwidth = Expression "Image width (pixels)" 64; 1968 nheight = Expression "Image height (pixels)" 64; 1969 factor = Scale "Factor" 0.001 1 0.2; 1970 1971 _result = Image (im_eye (to_real nwidth) (to_real nheight) 1972 factor.value); 1973 } 1974 } 1975 1976 Zone_plate = class 1977 Menuaction "_Zone Plate" "make a zone plate" { 1978 action = class 1979 _result { 1980 _vislevel = 3; 1981 1982 nsize = Expression "Image size (pixels)" 64; 1983 1984 _result = Image (im_zone (to_real nsize)); 1985 } 1986 } 1987 1988 Frequency_test_chart_item = class 1989 Menuaction "_Frequency Testchart" 1990 "make a black/white frequency test pattern" { 1991 action = class 1992 _result { 1993 _vislevel = 3; 1994 1995 nwidth = Expression "Image width (pixels)" 64; 1996 sheight = Expression "Strip height (pixels)" 10; 1997 waves = Expression "Wavelengths" [64, 32, 16, 8, 4, 2]; 1998 1999 _result 2000 = imagearray_assemble 0 0 (transpose [strips]) 2001 { 2002 freq_slice wave = Image (sin (grey / wave) > 0); 2003 strips = map freq_slice waves.expr; 2004 grey = im_fgrey (to_real nwidth) (to_real sheight) * 2005 360 * (to_real nwidth); 2006 } 2007 } 2008 } 2009 2010 CRT_test_chart_item = class 2011 Menuaction "CRT _Phosphor Chart" 2012 "make an image for measuring phosphor colours" { 2013 action = class 2014 _result { 2015 _vislevel = 3; 2016 2017 brightness = Scale "Brightness" 0 255 200; 2018 psize = Expression "Patch size (pixels)" 32; 2019 2020 _result 2021 = Image (imagearray_assemble 0 0 [[green, red], [blue, white]]) 2022 { 2023 2024 black = image_new (to_real psize) (to_real psize) 1 2025 Image_format.FLOAT Image_coding.NOCODING 2026 Image_type.B_W 0 0 0; 2027 notblack = black + brightness; 2028 2029 green = black ++ notblack ++ black; 2030 red = notblack ++ black ++ black; 2031 blue = black ++ black ++ notblack; 2032 white = notblack ++ notblack ++ notblack; 2033 } 2034 } 2035 } 2036 2037 Greyscale_chart_item = class 2038 Menuaction "_Greyscale" "make a greyscale" { 2039 action = class 2040 _result { 2041 _vislevel = 3; 2042 2043 pwidth = Expression "Patch width" 8; 2044 pheight = Expression "Patch height" 8; 2045 npatches = Expression "Number of patches" 16; 2046 2047 _result 2048 = Image (image_set_type Image_type.B_W 2049 (clip2fmt Image_format.UCHAR wedge)) 2050 { 2051 wedge 2052 = 255 / (to_real npatches - 1) * 2053 (int) (strip?0 / to_real pwidth) 2054 { 2055 strip = make_xy (to_real pwidth * to_real npatches) pheight; 2056 } 2057 } 2058 } 2059 } 2060 2061 CMYK_test_chart_item = class 2062 Menuaction "_CMYK Wedges" "make a set of CMYK wedges" { 2063 action = class 2064 _result { 2065 _vislevel = 3; 2066 2067 pwidth = Expression "Patch width" 8; 2068 pheight = Expression "Patch height" 8; 2069 npatches = Expression "Number of patches" 16; 2070 2071 _result 2072 = Image (image_set_type Image_type.CMYK 2073 (clip2fmt Image_format.UCHAR strips)) 2074 { 2075 wedge 2076 = 255 / (to_real npatches - 1) * 2077 (int) (strip?0 / to_real pwidth) 2078 { 2079 strip = make_xy (to_real pwidth * to_real npatches) pheight; 2080 } 2081 2082 black = wedge * 0; 2083 2084 C = wedge ++ black ++ black ++ black; 2085 M = black ++ wedge ++ black ++ black; 2086 Y = black ++ black ++ wedge ++ black; 2087 K = black ++ black ++ black ++ wedge; 2088 2089 strips = imagearray_assemble 0 0 [[C],[M],[Y],[K]]; 2090 } 2091 } 2092 } 2093 2094 Colour_atlas_item = class 2095 Menuaction "_Colour Atlas" 2096 "make a grid of patches grouped around a colour" { 2097 action = class 2098 _result { 2099 _vislevel = 3; 2100 2101 start = Colour_picker "Lab" [50,0,0]; 2102 nstep = Expression "Number of steps" 9; 2103 ssize = Expression "Step size" 10; 2104 psize = Expression "Patch size" 32; 2105 sepsize = Expression "Separator size" 4; 2106 2107 _result 2108 = colour_transform_to (get_type start) blocks''' 2109 { 2110 size = (to_real nstep * 2 + 1) * to_real psize - 2111 to_real sepsize; 2112 xy = make_xy size size; 2113 2114 xy_grid = (xy % to_real psize) < 2115 (to_real psize - to_real sepsize); 2116 grid = xy_grid?0 & xy_grid?1; 2117 2118 blocks = (int) (to_real ssize * ((int) (xy / to_real psize))); 2119 lab_start = colour_transform_to Image_type.LAB start; 2120 blocks' = blocks - to_real nstep * to_real ssize + 2121 Vector (tl lab_start.value); 2122 blocks'' = hd lab_start.value ++ Image blocks'; 2123 blocks''' 2124 = image_set_type Image_type.LAB blocks'', Image grid 2125 = 0; 2126 } 2127 } 2128 } 2129} 2130 2131