1Filter_conv_item = class 2 Menupullright "_Convolution" "various spatial convolution filters" { 3 /* Some useful masks. 4 */ 5 filter_blur = Matrix_con 9 0 [[1, 1, 1], [1, 1, 1], [1, 1, 1]]; 6 filter_sharp = Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]; 7 filter_emboss = Matrix_con 1 128 [[-1, 0], [0, 1]]; 8 filter_laplacian = Matrix_con 1 128 9 [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]; 10 filter_sobel = Matrix_con 1 128 [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]; 11 filter_lindet = Matrix_con 1 0 [[1, 1, 1], [-2, -2, -2], [1, 1, 1]]; 12 13 Blur_item = class 14 Menuaction "_Blur" "3x3 blur of image" { 15 action x = map_unary (conv filter_blur) x; 16 } 17 18 Sharpen_item = class 19 Menuaction "_Sharpen" "3x3 sharpen of image" { 20 action x = map_unary (conv filter_sharp) x; 21 } 22 23 Emboss_item = class 24 Menuaction "_Emboss" "1 pixel displace emboss" { 25 action x = map_unary (conv filter_emboss) x; 26 } 27 28 Laplacian_item = class 29 Menuaction "_Laplacian" "3x3 laplacian edge detect" { 30 action x = map_unary (conv filter_laplacian) x; 31 } 32 33 Sobel_item = class 34 Menuaction "So_bel" "3x3 Sobel edge detect" { 35 action x 36 = map_unary sobel x 37 { 38 sobel im 39 = abs (a - 128) + abs (b - 128) 40 { 41 a = conv filter_sobel im; 42 b = conv (rot270 filter_sobel) im; 43 } 44 } 45 } 46 47/* 3x3 line detect of image 48diagonals should be scaled down by root(2) I guess 49Kirk 50*/ 51 Linedet_item = class 52 Menuaction "Li_ne Detect" "3x3 line detect" { 53 action x 54 = map_unary lindet x 55 { 56 lindet im 57 = foldr1 max_pair images 58 { 59 masks = take 4 (iterate rot45 filter_lindet); 60 images = map (converse conv im) masks; 61 } 62 } 63 } 64 65 Usharp_item = class 66 Menuaction "_Unsharp Mask" "cored sharpen of L only in LAB image" { 67 action x = class 68 _result { 69 _vislevel = 3; 70 71 size = Option "Radius" [ 72 "3 pixels", 73 "5 pixels", 74 "7 pixels", 75 "9 pixels", 76 "11 pixels", 77 "51 pixels" 78 ] 0; 79 80 smooth_threshold = Slider 0 5 1.5; 81 brighten_max = Slider 1 50 10; 82 darken_max = Slider 1 50 50; 83 flat_sharp = Slider (-2) 5 1; 84 jaggy_sharp = Slider (-2) 5 2; 85 86 _result 87 = map_unary process x 88 { 89 process in 90 = Image in''' 91 { 92 in' = colour_transform_to Image_type.LABS in.value; 93 in'' = sharpen [3, 5, 7, 9, 11, 51]?size smooth_threshold 94 brighten_max darken_max 95 flat_sharp jaggy_sharp in'; 96 in''' = colour_transform_to (get_type in) in''; 97 } 98 } 99 } 100 } 101 102 sep1 = Menuseparator; 103 104 Custom_blur_item = class 105 Menuaction "Custom B_lur" "blur with tuneable parameters" { 106 action x = class 107 _result { 108 _vislevel = 3; 109 110 radius = Slider 1 50 1; 111 shape = Option "Mask shape" [ 112 "Square", 113 "Gaussian" 114 ] 0; 115 116 _result 117 = map_unary process x 118 { 119 process in 120 = convsep mask in 121 { 122 mask 123 = matrix_blur radius.value, shape.value == 0 124 = matrix_gaussian_blur radius.value; 125 } 126 } 127 } 128 } 129 130 Custom_conv_item = class 131 Menuaction "Custom C_onvolution" 132 "convolution filter with tuneable parameters" { 133 action x = class 134 _result { 135 _vislevel = 3; 136 137 matrix = Matrix_con 1 0 [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; 138 separable 139 = Toggle "Seperable convolution" false, 140 matrix.width == 1 || matrix.height == 1 141 = false; 142 type = Option "Convolution type" ["Int", "Float"] 0; 143 144 _result 145 = map_unary process x 146 { 147 process in 148 = Image in' 149 { 150 conv_fn 151 = im_conv, !separable && type == 0 152 = im_convsep, separable && type == 0 153 = im_convf, !separable && type == 1 154 = im_convsepf, separable && type == 1 155 = error "boink!"; 156 in' = conv_fn in.value matrix; 157 } 158 } 159 } 160 } 161} 162 163Filter_rank_item = class 164 Menupullright "_Rank" "various rank filters" { 165 Median_item = class 166 Menuaction "_Median" "3x3 median rank filter" { 167 action x = map_unary (rank 3 3 5) x; 168 } 169 170 Image_rank_item = class 171 Menuaction "_Image Rank" "pixelwise rank a list or group of images" { 172 action x = class 173 _result { 174 _vislevel = 3; 175 176 select 177 = Expression "Rank" ((int) ((guess_size + 1) / 2)) 178 { 179 guess_size 180 = len x, is_list x 181 = len x.value, is_Group x 182 = 0; 183 } 184 185 // can't really iterate over groups ... since we allow a group 186 // argument 187 _result = rank_image select x; 188 } 189 } 190 191 Custom_rank_item = class 192 Menuaction "Custom _Rank" "rank filter with tuneable parameters" { 193 action x = class 194 _result { 195 _vislevel = 3; 196 197 width = Expression "Window width" 3; 198 height = Expression "Window height" 3; 199 select = Expression "Rank" 200 ((int) ((to_real width * to_real height + 1) / 2)); 201 202 _result 203 = map_unary process x 204 { 205 process in 206 = rank width height select in; 207 } 208 } 209 } 210} 211 212Filter_morphology_item = class 213 Menupullright "_Morphology" "various morphological filters" { 214 /* Some useful masks. 215 */ 216 mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; 217 mask4 = Matrix_mor [[128, 255, 128], [255, 255, 255], [128, 255, 128]]; 218 mask1 = Matrix_mor [[0, 0, 0], [0, 255, 0], [0, 0, 0]]; 219 thin = Matrix_mor [[0, 0, 0], [128, 255, 128], [255, 255, 255]]; 220 221 Threshold_item = class 222 Menuaction "Thres_hold" "simple image threshold" { 223 action x = class 224 _result { 225 _vislevel = 3; 226 227 threshold 228 = Slider 0 mx (mx / 2) 229 { 230 mx 231 = Image_format.maxval x.format, is_Image x 232 = 255; 233 } 234 235 _result = map_unary (more threshold.value) x; 236 } 237 } 238 239 sep1 = Menuseparator; 240 241 Dilate8_item = class 242 Menuaction "Dilate _8-connected" "dilate with an 8-connected mask" { 243 action x = map_unary (dilate mask8) x; 244 } 245 246 Dilate4_item = class 247 Menuaction "Dilate _4-connected" "dilate with a 4-connected mask" { 248 action x = map_unary (dilate mask4) x; 249 } 250 251 Erode8_item = class 252 Menuaction "_Erode 8-connected" "erode with an 8-connected mask" { 253 action x = map_unary (erode mask8) x; 254 } 255 256 Erode4_item = class 257 Menuaction "E_rode 4-connected" "erode with a 4-connected mask" { 258 action x = map_unary (erode mask4) x; 259 } 260 261 Custom_morph_item = class 262 Menuaction "Custom _Morphology" 263 "convolution morphological operator" { 264 action x = class 265 _result { 266 _vislevel = 3; 267 268 mask = mask4; 269 type = Option "Operation" ["Erode", "Dilate"] 1; 270 apply = Expression "Number of times to apply mask" 1; 271 272 _result 273 = map_unary morph x 274 { 275 morph image 276 = Image value' 277 { 278 fatmask = (iterate (dilate mask) mask)?(to_real apply - 1); 279 280 value' 281 = im_erode image.value fatmask, type.value == 0 282 = im_dilate image.value fatmask; 283 } 284 } 285 } 286 } 287 288 sep2 = Menuseparator; 289 290 Open_item = class 291 Menuaction "_Open" "open with an 8-connected mask" { 292 action x = map_unary (dilate mask8 @ erode mask8) x; 293 } 294 295 Close_item = class 296 Menuaction "_Close" "close with an 8-connected mask" { 297 action x = map_unary (erode mask8 @ dilate mask8) x; 298 } 299 300 Clean_item = class 301 Menuaction "C_lean" "remove 8-connected isolated points" { 302 action x 303 = map_unary clean x 304 { 305 clean x = x ^ erode mask1 x; 306 } 307 } 308 309 Thin_item = class 310 Menuaction "_Thin" "thin once" { 311 action x 312 = map_unary thinall x 313 { 314 masks = take 8 (iterate rot45 thin); 315 thin1 m x = x ^ erode m x; 316 thinall x = foldr thin1 x masks; 317 } 318 } 319} 320 321Filter_fourier_item = class 322 Menupullright "_Fourier" "various Fourier filters" { 323 preview_size = 64; 324 325 sense_option = Option "Sense" [ 326 "Pass", 327 "Reject" 328 ] 0; 329 330 // make a visualisation image 331 make_vis fn = (Image @ image_set_type Image_type.FOURIER @ rotquad @ fn) 332 (im_create_fmask preview_size preview_size); 333 334 // make the process function 335 process fn in 336 = (Image @ fn) (im_flt_image_freq in.value); 337 338 New_ideal_item = class 339 Menupullright "_Ideal" "various ideal Fourier filters" { 340 High_low_item = class 341 Menuaction "_High or Low Pass" 342 "highpass/lowpass ideal Fourier filter" { 343 action x = class 344 _result { 345 _vislevel = 3; 346 347 sense = sense_option; 348 frequency_cutoff = Slider 0.01 0.99 0.5; 349 350 // call a freq func with our parameters 351 _params f = f sense.value frequency_cutoff.value 0 0 0 0; 352 353 visualize_mask = make_vis _params; 354 355 _result = map_unary (process _params) x; 356 } 357 } 358 359 Ring_item = class 360 Menuaction "_Ring Pass or Ring Reject" 361 "ring pass/reject ideal Fourier filter" { 362 action x = class 363 _result { 364 _vislevel = 3; 365 366 sense = sense_option; 367 frequency_cutoff = Slider 0.01 0.99 0.5; 368 ring_width = Slider 0.01 0.99 0.5; 369 370 // call a freq func with our parameters 371 _params f = f (sense.value + 6) frequency_cutoff.value 372 ring_width.value 0 0 0; 373 374 visualize_mask = make_vis _params; 375 376 _result = map_unary (process _params) x; 377 } 378 } 379 380 Band_item = class 381 Menuaction "_Band Pass or Band Reject" 382 "band pass/reject ideal Fourier filter" { 383 action x = class 384 _result { 385 _vislevel = 3; 386 387 sense = sense_option; 388 frequency_cutoff_x = Slider 0.01 0.99 0.5; 389 frequency_cutoff_y = Slider 0.01 0.99 0.5; 390 radius = Slider 0.01 0.99 0.5; 391 392 // call a freq func with our parameters 393 _params f = f (sense.value + 12) 394 frequency_cutoff_x.value frequency_cutoff_y.value 395 radius.value 0 0; 396 397 visualize_mask = make_vis _params; 398 399 _result = map_unary (process _params) x; 400 } 401 } 402 } 403 404 New_gaussian_item = class 405 Menupullright "_Gaussian" "various Gaussian Fourier filters" { 406 High_low_item = class 407 Menuaction "_High or Low Pass" 408 "highpass/lowpass Gaussian Fourier filter" { 409 action x = class 410 _result { 411 _vislevel = 3; 412 413 sense = sense_option; 414 frequency_cutoff = Slider 0.01 0.99 0.5; 415 amplitude_cutoff = Slider 0.01 0.99 0.5; 416 417 // call a freq func with our parameters 418 _params f = f (sense.value + 4) frequency_cutoff.value 419 amplitude_cutoff.value 0 0 0; 420 421 visualize_mask = make_vis _params; 422 423 _result = map_unary (process _params) x; 424 } 425 } 426 427 Ring_item = class 428 Menuaction "_Ring Pass or Ring Reject" 429 "ring pass/reject Gaussian Fourier filter" { 430 action x = class 431 _result { 432 _vislevel = 3; 433 434 sense = sense_option; 435 frequency_cutoff = Slider 0.01 0.99 0.5; 436 amplitude_cutoff = Slider 0.01 0.99 0.5; 437 ring_width = Slider 0.01 0.99 0.5; 438 439 // call a freq func with our parameters 440 _params f = f (sense.value + 10) frequency_cutoff.value 441 ring_width.value amplitude_cutoff.value 0 0; 442 443 visualize_mask = make_vis _params; 444 445 _result = map_unary (process _params) x; 446 } 447 } 448 449 Band_item = class 450 Menuaction "_Band Pass or Band Reject" 451 "band pass/reject Gaussian Fourier filter" { 452 action x = class 453 _result { 454 _vislevel = 3; 455 456 sense = sense_option; 457 frequency_cutoff_x = Slider 0.01 0.99 0.5; 458 frequency_cutoff_y = Slider 0.01 0.99 0.5; 459 radius = Slider 0.01 0.99 0.5; 460 amplitude_cutoff = Slider 0.01 0.99 0.5; 461 462 // call a freq func with our parameters 463 _params f = f (sense.value + 16) 464 frequency_cutoff_x.value frequency_cutoff_y.value 465 radius.value amplitude_cutoff.value 0; 466 467 visualize_mask = make_vis _params; 468 469 _result = map_unary (process _params) x; 470 } 471 } 472 } 473 474 New_butterworth_item = class 475 Menupullright "_Butterworth" 476 "various Butterworth Fourier filters" { 477 High_low_item = class 478 Menuaction "_High or Low Pass" 479 "highpass/lowpass Butterworth Fourier filter" { 480 action x = class 481 _result { 482 _vislevel = 3; 483 484 sense = sense_option; 485 frequency_cutoff = Slider 0.01 0.99 0.5; 486 amplitude_cutoff = Slider 0.01 0.99 0.5; 487 order = Slider 1 10 2; 488 489 // call a freq func with our parameters 490 _params f = f (sense.value + 2) 491 order.value 492 frequency_cutoff.value amplitude_cutoff.value 493 0 0; 494 495 visualize_mask = make_vis _params; 496 497 _result = map_unary (process _params) x; 498 } 499 } 500 501 Ring_item = class 502 Menuaction "_Ring Pass or Ring Reject" 503 "ring pass/reject Butterworth Fourier filter" { 504 action x = class 505 _result { 506 _vislevel = 3; 507 508 sense = sense_option; 509 frequency_cutoff = Slider 0.01 0.99 0.5; 510 amplitude_cutoff = Slider 0.01 0.99 0.5; 511 ring_width = Slider 0.01 0.99 0.5; 512 order = Slider 1 10 2; 513 514 // call a freq func with our parameters 515 _params f = f (sense.value + 8) 516 order.value frequency_cutoff.value ring_width.value 517 amplitude_cutoff.value 0; 518 519 visualize_mask = make_vis _params; 520 521 _result = map_unary (process _params) x; 522 } 523 } 524 525 Band_item = class 526 Menuaction "_Band Pass or Band Reject" 527 "band pass/reject Butterworth Fourier filter" { 528 action x = class 529 _result { 530 _vislevel = 3; 531 532 sense = sense_option; 533 frequency_cutoff_x = Slider 0.01 0.99 0.5; 534 frequency_cutoff_y = Slider 0.01 0.99 0.5; 535 radius = Slider 0.01 0.99 0.5; 536 amplitude_cutoff = Slider 0.01 0.99 0.5; 537 order = Slider 1 10 2; 538 539 // call a freq func with our parameters 540 _params f = f (sense.value + 14) order.value 541 frequency_cutoff_x.value frequency_cutoff_y.value 542 radius.value amplitude_cutoff.value; 543 544 visualize_mask = make_vis _params; 545 546 _result = map_unary (process _params) x; 547 } 548 } 549 } 550} 551 552Filter_enhance_item = class 553 Menupullright "_Enhance" "various enhancement filters" { 554 Falsecolour_item = class 555 Menuaction "_False Colour" "false colour a mono image" { 556 action x = class 557 _result { 558 _vislevel = 3; 559 560 offset = Slider (-255) 255 0; 561 clip = Toggle "Clip colour range" false; 562 563 _result 564 = map_unary process x 565 { 566 process im 567 = falsecolour mono'' 568 { 569 mono = colour_transform_to Image_type.B_W im; 570 mono' = mono + offset; 571 mono'' 572 = (unsigned char) mono', clip 573 = (unsigned char) (mono' & 0xff); 574 } 575 } 576 } 577 } 578 579 Statistical_diff_item = class 580 Menuaction "_Statistical Difference" 581 "statistical difference of an image" { 582 action x = class 583 _result { 584 _vislevel = 3; 585 586 wsize = Expression "Window size" 11; 587 tmean = Expression "Target mean" 128; 588 mean_weight = Slider 0 1 0.8; 589 tdev = Expression "Target deviation" 50; 590 dev_weight = Slider 0 1 0.8; 591 border = Toggle "Output image matches input image in size" true; 592 593 _result 594 = map_unary process x 595 { 596 process in 597 = Image in'' 598 { 599 in' = colour_transform_to Image_type.B_W in.value; 600 fn 601 = im_stdif, border 602 = im_stdif_raw; 603 in'' = fn in' 604 mean_weight.value tmean.expr 605 dev_weight.value tdev.expr 606 wsize.expr wsize.expr; 607 } 608 } 609 } 610 } 611 612 Hist_equal_item = class 613 Menupullright "_Equalise Histogram" "equalise contrast" { 614 Global_item = class 615 Menuaction "_Global" "equalise contrast globally" { 616 action x = map_unary hist_equalize x; 617 } 618 619 Local_item = class 620 Menuaction "_Local" "equalise contrast within a roving window" { 621 action x = class 622 _result { 623 _vislevel = 3; 624 625 width = Expression "Window width" 20; 626 height = Expression "Window height" 20; 627 628 _result 629 = map_unary process x 630 { 631 process in 632 = hist_equalize_local width.expr height.expr in; 633 } 634 } 635 } 636 } 637} 638 639#separator 640 641Filter_tilt_item = class 642 Menupullright "Ti_lt Brightness" "tilt brightness" { 643 Left_right_item = class 644 Menuaction "_Left to Right" "linear left-right brighten" { 645 action x = class 646 _result { 647 _vislevel = 3; 648 649 tilt = Slider (-1) 1 0; 650 651 _result 652 = map_unary tilt_lr x 653 { 654 tilt_lr image 655 = image * scale 656 { 657 ramp = im_fgrey image.width image.height; 658 scale = (ramp - 0.5) * tilt + 1; 659 } 660 } 661 } 662 } 663 664 Top_bottom_item = class 665 Menuaction "_Top to Bottom" "linear top-bottom brighten" { 666 action x = class 667 _result { 668 _vislevel = 3; 669 670 tilt = Slider (-1) 1 0; 671 672 _result 673 = map_unary tilt_tb x 674 { 675 tilt_tb image 676 = image * scale 677 { 678 ramp = rot90 679 (im_fgrey image.height image.width); 680 scale = (ramp - 0.5) * tilt + 1; 681 } 682 } 683 } 684 } 685 686 sep1 = Menuseparator; 687 688 Left_right_cos_item = class 689 Menuaction "Cosine Left-_right" "cosine left-right brighten" { 690 action x = class 691 _result { 692 _vislevel = 3; 693 694 tilt = Slider (-1) 1 0; 695 shift = Slider (-1) 1 0; 696 697 _result 698 = map_unary tilt_lr x 699 { 700 tilt_lr image 701 = image * scale 702 { 703 ramp = im_fgrey image.width image.height - 0.5 - 704 shift.value; 705 scale = 0.5 * tilt.value * cos (ramp * 180) + 1; 706 } 707 } 708 } 709 } 710 711 Top_bottom_cos_item = class 712 Menuaction "Cosine Top-_bottom" "cosine top-bottom brighten" { 713 action x = class 714 _result { 715 _vislevel = 3; 716 717 tilt = Slider (-1) 1 0; 718 shift = Slider (-1) 1 0; 719 720 _result 721 = map_unary tilt_tb x 722 { 723 tilt_tb image 724 = image * scale 725 { 726 ramp = rot90 (im_fgrey image.height image.width) - 0.5 - 727 shift.value; 728 scale = 0.5 * tilt.value * cos (ramp * 180) + 1; 729 } 730 } 731 } 732 } 733 734 sep2 = Menuseparator; 735 736 Circular_item = class 737 Menuaction "_Circular" "circular brighten" { 738 action x = class 739 _result { 740 _vislevel = 3; 741 742 tilt = Slider (-1) 1 0; 743 hshift = Slider (-1) 1 0; 744 vshift = Slider (-1) 1 0; 745 746 _result 747 = map_unary tilt_tb x 748 { 749 tilt_tb image 750 = image * scale 751 { 752 hramp = im_fgrey image.width image.height - 0.5 - 753 hshift.value; 754 vramp = rot90 (im_fgrey image.height image.width) - 0.5 - 755 vshift.value; 756 ramp = (hramp ** 2 + vramp ** 2) ** 0.5; 757 scale = 0.5 * tilt.value * cos (ramp * 180) + 1; 758 } 759 } 760 } 761 } 762} 763 764Filter_blend_item = class 765 Menupullright "_Blend" "blend objects together" { 766 Slider_blend_item = class 767 Menuaction "_Slider Blend" "blend two objects together with a slider" { 768 action a b = class 769 _result { 770 _vislevel = 3; 771 772 blend = Slider 0 1 0.5; 773 774 _result 775 = map_binary process a b 776 { 777 process im1 im2 = im1 * (1 - blend) + im2 * blend; 778 } 779 } 780 } 781 782 Image_blend_item = class 783 Menuaction "_Image Blend" "use an image to blend two objects" { 784 action a b c 785 = map_trinary process a b c 786 { 787 process a b c 788 = blend condition in1 in2 789 { 790 compare a b 791 // prefer image as the condition 792 = false, 793 !has_image a && has_image b 794 // prefer mono images as the condition 795 = false, 796 has_bands a && has_bands b && 797 get_bands a > 1 && get_bands b == 1 798 // prefer uchar as the condition 799 = false, 800 has_format a && has_format b && 801 get_format a > Image_format.UCHAR && 802 get_format b == Image_format.UCHAR 803 = true; 804 args' = sortc compare [a, b, c]; 805 condition = args'?0; 806 in1 = args'?1; 807 in2 = args'?2; 808 } 809 } 810 } 811 812 Line_blend_item = class 813 Menuaction "_Along Line" 814 "blend between image a and image b along a line" { 815 action a b = class 816 _result { 817 _vislevel = 3; 818 819 orientation = Option "Orientation" [ 820 "Left to Right", 821 "Top to Bottom" 822 ] 0; 823 blend_position = Slider 0 1 0.5; 824 blend_width = Slider 0 1 0.05; 825 826 _result 827 = map_binary process a b 828 { 829 process a b 830 = blend (Image condition) b a 831 { 832 output_width = max_pair a.width b.width; 833 output_height = max_pair a.height b.height; 834 range 835 = output_width, orientation == 0 836 = output_height; 837 blend_position' 838 = floor (range * blend_position.value); 839 blend_width' 840 = 1, blend_width.value == 0 841 = floor (range * blend_width.value); 842 start = blend_position' - blend_width' / 2; 843 844 background = (make_xy output_width output_height) >= 845 blend_position'; 846 ramp 847 = im_grey blend_width' output_height, orientation == 0 848 = rot90 (im_grey blend_width' output_width); 849 condition 850 = insert_noexpand start 0 ramp background?0, 851 orientation == 0 852 = insert_noexpand 0 start ramp background?1; 853 } 854 } 855 } 856 } 857} 858 859Filter_overlay_header_item = class 860 Menuaction "_Overlay" 861 "make a colour overlay of two monochrome images" { 862 action a b = class 863 _result { 864 _vislevel = 3; 865 866 colour = Option "Colour overlay as" [ 867 "Green over Red", 868 "Blue over Red", 869 "Red over Green", 870 "Red over Blue", 871 "Blue over Green", 872 "Green over Blue" 873 ] 0; 874 875 _result 876 = map_binary overlay a b 877 { 878 overlay a b 879 = image_set_type Image_type.sRGB 880 [(a' ++ b' ++ 0), 881 (a' ++ 0 ++ b'), 882 (b' ++ a' ++ 0), 883 (b' ++ 0 ++ a'), 884 (0 ++ a' ++ b'), 885 (0 ++ b' ++ a')]?colour 886 { 887 a' = colour_transform_to Image_type.B_W a; 888 b' = colour_transform_to Image_type.B_W b; 889 } 890 } 891 } 892} 893 894Filter_colourize_item = class 895 Menuaction "_Colourize" "use a colour image or patch to tint a mono image" { 896 action a b = class 897 _result { 898 _vislevel = 3; 899 900 tint = Slider 0 1 0.6; 901 902 _result 903 = map_binary tintit a b 904 { 905 tintit a b 906 = colour_transform_to (get_type colour) colourized' 907 { 908 // get the mono thing first 909 args = sortc (const (is_colour_type @ get_type)) [a, b]; 910 mono = args?0; 911 colour = args?1; 912 913 colour' = tint * colour_transform_to Image_type.LAB colour; 914 mono' = colour_transform_to Image_type.B_W mono; 915 colourized = (mono' / 2.55) ++ colour'?1 ++ colour'?2; 916 colourized' = image_set_type Image_type.LAB colourized; 917 } 918 } 919 } 920} 921 922Filter_browse_multiband_item = class 923 Menupullright "Bro_wse" "browse though an image, bitwise or bandwise" { 924 Bandwise_item = class 925 Menuaction "B_andwise" "browse through the bands of a multiband image" { 926 action image = class 927 _result { 928 _vislevel = 3; 929 930 band = Slider 0 (image.bands - 1) 0; 931 display = Option "Display as" [ 932 "Grey", 933 "Green over Red", 934 "Blue over Red", 935 "Red over Green", 936 "Red over Blue", 937 "Blue over Green", 938 "Green over Blue" 939 ] 0; 940 941 _result 942 = output 943 { 944 down = (int) band.value; 945 up = down + 1; 946 remainder = band.value - down; 947 948 fade x a 949 = Vector [0], x == 0 950 = a * x; 951 952 a = fade remainder image?up; 953 b = fade (1 - remainder) image?down; 954 955 output = [ 956 a + b, 957 a ++ b ++ 0, 958 a ++ 0 ++ b, 959 b ++ a ++ 0, 960 b ++ 0 ++ a, 961 0 ++ a ++ b, 962 0 ++ b ++ a 963 ] ? display; 964 } 965 } 966 } 967 968 Bitwise_item = class 969 Menuaction "Bi_twise" "browse through the bits of an image" { 970 action x = class 971 _result { 972 _vislevel = 3; 973 974 bit 975 = Islider 0 (nbits - 1) (nbits - 1) 976 { 977 nbits 978 = x.bits, is_Image x 979 = 8; 980 Islider f t v = class 981 scope.Slider f t ((int) v) { 982 Slider = Islider; 983 } 984 } 985 986 _result 987 = map_unary process x 988 { 989 process im = (im & (0x1 << bit.value)) != 0; 990 } 991 } 992 } 993} 994 995#separator 996 997Filter_negative_item = class 998 Menuaction "Photographic _Negative" "swap black and white" { 999 action x 1000 = map_unary invert x 1001 { 1002 invert in 1003 = clip2fmt in.format (colour_transform_to (get_type in) rgb') 1004 { 1005 rgb = colour_transform_to Image_type.sRGB in; 1006 rgb' = 255 - rgb; 1007 } 1008 } 1009} 1010 1011Filter_solarize_item = class 1012 Menuaction "_Solarise" "invert colours above a threshold" { 1013 action x = class 1014 _result { 1015 _vislevel = 3; 1016 1017 kink = Slider 0 1 0.5; 1018 1019 _result 1020 = map_unary process x 1021 { 1022 process image 1023 = hist_map tab'''' image 1024 { 1025 // max pixel value for this format 1026 mx = Image_format.maxval image.format; 1027 1028 // make a LUT ... just 8 and 16 bit 1029 tab 1030 = im_identity_ushort image.bands mx, 1031 image.format == 1032 Image_format.USHORT 1033 = im_identity image.bands; 1034 tab' = Image tab; 1035 1036 // make basic ^ shape 1037 tab'' 1038 = tab' * (1 / kink), tab' < mx * kink 1039 = (mx - tab') / (1 - kink); 1040 tab''' = clip2fmt image.format tab''; 1041 1042 // smooth a bit 1043 mask = matrix_blur (tab'''.width / 8); 1044 tab'''' = convsep mask tab'''; 1045 } 1046 } 1047 } 1048} 1049 1050Filter_diffuse_glow_item = class 1051 Menuaction "_Diffuse Glow" "add a halo to highlights" { 1052 action x = class 1053 _result { 1054 _vislevel = 3; 1055 1056 radius = Slider 0 50 5; 1057 highlights = Slider 0 100 95; 1058 glow = Slider 0 1 0.5; 1059 colour = Colour_new_item.Widget_colour_item.action; 1060 1061 _result 1062 = map_unary process x 1063 { 1064 process image 1065 = image' 1066 { 1067 mono = (unsigned char) (colour_transform_to 1068 Image_type.B_W image); 1069 thresh = hist_thresh (highlights.value / 100) mono; 1070 mask = mono > thresh; 1071 blur = convsep (matrix_gaussian_blur 1072 radius.value) mask; 1073 colour' = colour_transform_to 1074 image.type colour; 1075 image' = image + colour' * glow * (blur / 255); 1076 } 1077 } 1078 } 1079} 1080 1081Filter_drop_shadow_item = class 1082 Menuaction "Drop S_hadow" "add a drop shadow to an image" { 1083 action x = class 1084 _result { 1085 _vislevel = 3; 1086 1087 shadow_x = Slider (-50) 50 5; 1088 shadow_y = Slider (-50) 50 5; 1089 shadow_softness = Slider 0 20 5; 1090 bg_colour = Expression "Background colour" 255; 1091 sd_colour = Expression "Shadow colour" 128; 1092 alpha = Toggle "Shadow in alpha channel" false; 1093 transparent = Toggle "Zero pixels are transparent" false; 1094 1095 _result 1096 = map_unary shadow x 1097 { 1098 shadow image 1099 = Image final 1100 { 1101 blur_size = shadow_softness.value * 2 + 1; 1102 1103 // matrix we blur with to soften shadows 1104 blur_matrix = matrix_gaussian_blur blur_size; 1105 matrix_size = blur_matrix.width; 1106 matrix_radius = (int) (matrix_size / 2) + 1; 1107 1108 // position and size of shadow image in input cods 1109 // before and after fuzzing 1110 shadow_rect = Rect shadow_x.value shadow_y.value 1111 image.width image.height; 1112 fuzzy_shadow_rect = shadow_rect.margin_adjust matrix_radius; 1113 1114 // size and pos of final image, in input cods 1115 final_rect = image.rect.union fuzzy_shadow_rect; 1116 1117 // hard part of shadow in output cods 1118 shadow_rect' = Rect 1119 (shadow_rect.left - final_rect.left) 1120 (shadow_rect.top - final_rect.top) 1121 shadow_rect.width shadow_rect.height; 1122 1123 // make the shadow mask ... true for parts which cast 1124 // a shadow 1125 mask 1126 = (foldr1 bitwise_and @ bandsplit) (image.value != 0), 1127 transparent 1128 = image_new image.width image.height 1 Image_format.UCHAR 1129 Image_coding.NOCODING Image_type.B_W 255 0 0; 1130 mask' = embed 0 shadow_rect'.left shadow_rect'.top 1131 final_rect.width final_rect.height mask; 1132 mask'' = convsep blur_matrix mask'; 1133 1134 // use mask to fade between bg and shadow colour 1135 mk_background colour = image_new 1136 final_rect.width final_rect.height 1137 image.bands image.format image.coding image.type 1138 colour 0 0; 1139 1140 bg_image = mk_background bg_colour.expr; 1141 shadow_image = mk_background sd_colour.expr; 1142 bg = blend mask'' shadow_image bg_image; 1143 1144 // make a full size mask 1145 fg_mask = embed 0 1146 (image.rect.left - final_rect.left) 1147 (image.rect.top - final_rect.top) 1148 final_rect.width final_rect.height mask; 1149 1150 // wrap up the input image ... put the shadow colour 1151 // around it, so if we are outputting a separate 1152 // alpha the shadow colour will be set correctly 1153 fg = insert (image.rect.left - final_rect.left) 1154 (image.rect.top - final_rect.top) 1155 image.value shadow_image; 1156 1157 final 1158 // make a separate alpha 1159 = fg ++ mask'', alpha 1160 1161 // paste image over shadow 1162 = if fg_mask then fg else bg; 1163 } 1164 } 1165 } 1166} 1167 1168Filter_paint_text_item = class 1169 Menuaction "_Paint Text" "paint text into an image" { 1170 action x 1171 = paint_position, is_Group x 1172 = paint_area 1173 { 1174 paint_area = class 1175 _result { 1176 _check_args = [ 1177 [x, "x", check_Image] 1178 ]; 1179 _vislevel = 3; 1180 1181 text = String "Text to paint" "<i>Hello</i> world!"; 1182 font = Fontname "Use font" Workspaces.Preferences.PAINTBOX_FONT; 1183 align = Option "Alignment" ["Left", "Centre", "Right"] 0; 1184 dpi = Expression "DPI" 300; 1185 colour = Expression "Text colour" 255; 1186 place = Region x (x.width / 4) (x.height / 4) 1187 (x.width / 2) (x.height / 2); 1188 1189 _result 1190 = insert_noexpand place.left place.top (blend txt' fg place) x 1191 { 1192 fg = image_new place.width place.height x.bands x.format 1193 x.coding x.type colour.expr 0 0; 1194 txt = Image (im_text text.value font.value 1195 place.width align.value (to_real dpi)); 1196 bg = im_black place.width place.height 1; 1197 txt' = insert_noexpand 0 0 txt bg; 1198 } 1199 } 1200 1201 paint_position = class 1202 _result { 1203 _vislevel = 3; 1204 1205 text = Pattern_images_item.Text_item.action; 1206 colour = Expression "Text colour" 255; 1207 position = Option "Position" [ 1208 "North-west", 1209 "North", 1210 "North-east", 1211 "West", 1212 "Centre", 1213 "East", 1214 "South-west", 1215 "South", 1216 "South-east", 1217 "Specify in pixels" 1218 ] 4; 1219 left = Expression "Pixels from left" 0; 1220 top = Expression "Pixels from top" 0; 1221 1222 _result 1223 = map_unary paint x 1224 { 1225 paint image 1226 = insert_noexpand x' y' place' image 1227 { 1228 xr = image.width - text.width; 1229 yr = image.height - text.height; 1230 x 1231 = left.expr, position == 9 1232 = [0, xr / 2, xr]?(position % 3); 1233 y 1234 = top.expr, position == 9 1235 = [0, yr / 2, yr]?(position / 3); 1236 x' = range 0 x (image.width - 1); 1237 y' = range 0 y (image.height - 1); 1238 w' = range 1 text.width (image.width - x'); 1239 h' = range 1 text.height (image.height - y'); 1240 1241 place = extract_area x' y' w' h' image; 1242 text' = insert_noexpand 0 0 text (im_black w' h' 1); 1243 fg = image_new w' h' image.bands image.format 1244 image.coding image.type colour.expr 0 0; 1245 place' = blend text' fg place; 1246 } 1247 } 1248 } 1249 } 1250} 1251