1/* Various operators as functions. 2 */ 3 4logical_and a b = a && b; 5logical_or a b = a || b; 6bitwise_and a b = a & b; 7bitwise_or a b = a | b; 8eor a b = a ^ b; 9left_shift a b = a << b; 10right_shift a b = a >> b; 11not a = !a; 12 13less a b = a < b; 14more a b = a > b; 15less_equal a b = a <= b; 16more_equal a b = a >= b; 17equal a b = a == b; 18not_equal a b = a != b; 19pointer_equal a b = a === b; 20not_pointer_equal a b = a !== b; 21 22add a b = a + b; 23subtract a b = a - b; 24multiply a b = a * b; 25divide a b = a / b; 26idivide a b = (int) ((int) a / (int) b); 27power a b = a ** b; 28square x = x * x; 29remainder a b = a % b; 30 31cons a b = a : b; 32dot a b = a . ( b ); 33join a b = a ++ b; 34// 'difference' is defined in _list 35subscript a b = a ? b; 36 37generate s n f = [s, n .. f]; 38comma r i = (r, i); 39 40compose f g = f @ g; 41 42// our only trinary operator is actually a binary operator 43if_then_else a x = if a then x?0 else x?1; 44 45cast_unsigned_char x = (unsigned char) x; 46cast_signed_char x = (signed char) x; 47cast_unsigned_short x = (unsigned short) x; 48cast_signed_short x = (signed short) x; 49cast_unsigned_int x = (unsigned int) x; 50cast_signed_int x = (signed int) x; 51cast_float x = (float) x; 52cast_double x = (double) x; 53cast_complex x = (complex) x; 54cast_double_complex x = (double complex) x; 55 56unary_minus x = -x; 57negate x = !x; 58complement x = ~x; 59unary_plus x = +x; 60 61// the function we call for "a -> v" expressions 62mksvpair s v 63 = [s, v], is_string s 64 = error "not str on lhs of ->"; 65 66// the vector ops ... im is an image, vec is a real_list 67vec op_name im vec 68 = im_lintra_vec ones im vec, 69 op_name == "add" || op_name == "add'" 70 = im_lintra_vec ones (-1 * im) vec, 71 op_name == "subtract'" 72 = im_lintra_vec ones im inv, 73 op_name == "subtract" 74 = im_lintra_vec vec im zeros, 75 op_name == "multiply" || op_name == "multiply'" 76 = im_lintra_vec vec (1 / im) zeros, 77 op_name == "divide'" 78 = im_lintra_vec recip im zeros, 79 op_name == "divide" 80 = im_expntra_vec im vec, 81 op_name == "power'" 82 = im_powtra_vec im vec, 83 op_name == "power" 84 = im_remainderconst_vec im vec, 85 op_name == "remainder" 86 = im_andimage_vec im vec, 87 op_name == "bitwise_and" || op_name == "bitwise_and'" 88 = im_orimage_vec im vec, 89 op_name == "bitwise_or" || op_name == "bitwise_or'" 90 = im_eorimage_vec im vec, 91 op_name == "eor" || op_name == "eor'" 92 = im_equal_vec im vec, 93 op_name == "equal" || op_name == "equal'" 94 = im_notequal_vec im vec, 95 op_name == "not_equal" || op_name == "not_equal'" 96 = im_less_vec im vec, 97 op_name == "less" 98 = im_moreeq_vec im vec, 99 op_name == "less'" 100 = im_lesseq_vec im vec, 101 op_name == "less_equal" 102 = im_more_vec im vec, 103 op_name == "less_equal'" 104 = error ("unimplemented vector operation: " ++ op_name) 105{ 106 zeros = replicate (len vec) 0; 107 ones = replicate (len vec) 1; 108 recip = map (divide 1) vec; 109 inv = map (multiply (-1)) vec; 110} 111 112// make a name value pair 113mknvpair n v 114 = [n, v], is_string n 115 = error "not [char] on LHS of =>"; 116 117/* Macbeth chart patch names. 118 */ 119macbeth_names = [ 120 "Dark skin", 121 "Light skin", 122 "Blue sky", 123 "Foliage", 124 "Blue flower", 125 "Bluish green", 126 "Orange", 127 "Purplish blue", 128 "Moderate red", 129 "Purple", 130 "Yellow green", 131 "Orange yellow", 132 "Blue", 133 "Green", 134 "Red", 135 "Yellow", 136 "Magenta", 137 "Cyan", 138 "White (density 0.05)", 139 "Neutral 8 (density 0.23)", 140 "Neutral 6.5 (density 0.44)", 141 "Neutral 5 (density 0.70)", 142 "Neutral 3.5 (density 1.05)", 143 "Black (density 1.50)" 144]; 145 146bandsplit x 147 = oo_unary_function bandsplit_op x, is_class x 148 = map (subscript x) [0 .. bands - 1], is_image x 149 = error (_ "bad arguments to " ++ "bandsplit") 150{ 151 bands = get_header "Bands" x; 152 bandsplit_op = Operator "bandsplit" (map Image @ bandsplit) 153 Operator_type.COMPOUND false; 154} 155 156bandjoin l 157 = wrapper joined, has_wrapper 158 = joined, is_listof has_image l 159 = error (_ "bad arguments to " ++ "bandjoin") 160{ 161 has_wrapper = has_member_list (has_member "Image") l; 162 wrapper = get_member_list (has_member "Image") (get_member "Image") l; 163 joined = im_gbandjoin (map get_image l); 164} 165 166sum x 167 = oo_unary_function sum_op x, is_class x 168 = im_avg x * (get_width x) * (get_height x) * (get_bands x), is_image x 169 = sum_list x, is_list x 170 = error (_ "bad arguments (" ++ print x ++ ") to " ++ "sum") 171{ 172 sum_op = Operator "sum" sum Operator_type.COMPOUND false; 173 174 // add elements in a nested-list thing 175 sum_list l 176 = foldr acc 0 l 177 { 178 acc x total 179 = total + sum x, is_list x 180 = total + x; 181 } 182} 183 184product x 185 = oo_unary_function product_op x, is_class x 186 = product_list x, is_list x 187 // (product image) doesn't make much sense :( 188 = error (_ "bad arguments (" ++ print x ++ ") to " ++ "product") 189{ 190 product_op = Operator "product" product Operator_type.COMPOUND false; 191 192 product_list l 193 = foldr prod 1 l 194 { 195 prod x total 196 = total * product x, is_list x 197 = total * x; 198 } 199} 200 201mean x 202 = oo_unary_function mean_op x, is_class x 203 = im_avg x, is_image x 204 = mean_list x, is_list x 205 = error (_ "bad arguments (" ++ print x ++ ") to " ++ "mean") 206{ 207 mean_op = Operator "mean" mean Operator_type.COMPOUND false; 208 209 mean_list l = sum l / size l; 210 211 // number of elements in some sort of nested-list thing 212 size l 213 = foldr acc 0 l 214 { 215 acc x total 216 = total + size x, is_list x 217 = total + 1; 218 } 219} 220 221meang x 222 = (appl (power e) @ mean @ appl log) x 223{ 224 appl fn x 225 = map fn x, is_list x 226 = fn x; 227} 228 229// zero-excluding mean 230meanze x 231 = oo_unary_function meanze_op x, is_class x 232 = meanze_image_hist x, is_image x && 233 (fmt == Image_format.UCHAR || fmt == Image_format.USHORT) 234 = meanze_image x, is_image x 235 = meanze_list x, is_list x 236 = error (_ "bad arguments (" ++ print x ++ ") to " ++ "meanze") 237{ 238 fmt = get_format x; 239 240 meanze_op = Operator "meanze" meanze Operator_type.COMPOUND false; 241 242 meanze_list l = sum l / size l; 243 244 // number of non-zero elements in some sort of nested-list thing 245 size l 246 = foldr acc 0 l 247 { 248 acc x total 249 = total + size x, is_list x 250 = total + 1, x != 0; 251 = total; 252 } 253 254 // add elements in a nested-list thing 255 sum l 256 = foldr acc 0 l 257 { 258 acc x total 259 = total + sum x, is_list x 260 = total + x; 261 } 262 263 // image mean, for any image type 264 meanze_image i 265 = sum / N 266 { 267 w = get_width i; 268 h = get_height i; 269 b = get_bands i; 270 271 st = stats i; 272 sum = st.value?0?2; 273 274 // find non-zero pixels (not zero in all bands) 275 zp = im_notequal_vec i (replicate b 0); 276 277 // number of non-zero pixels 278 N = b * (mean zp * w * h) / 255; 279 } 280 281 // image mean for 8 and 16-bit unsigned images 282 // we can use a histogram, yay, and save a pass through the image 283 meanze_image_hist i 284 = sum / N 285 { 286 // histogram, knock out zeros 287 hist = hist_find i; 288 black = image_new 1 1 (get_bands hist) 289 (get_format hist) (get_coding hist) (get_type hist) 0 0 0; 290 histze = insert 0 0 black hist; 291 292 // matching identity 293 iden 294 = im_identity_ushort (get_bands hist) (get_width hist), 295 (get_width hist) > 256 296 = im_identity (get_bands hist); 297 298 // number of non-zero pixels 299 N = mean histze * 256; 300 301 // sum of pixels 302 sum = mean (hist * iden) * 256; 303 } 304} 305 306deviation x 307 = oo_unary_function deviation_op x, is_class x 308 = im_deviate x, is_image x 309 = deviation_list x, is_real_list x || is_matrix x 310 = error (_ "bad arguments to " ++ "deviation") 311{ 312 deviation_op = Operator "deviation" 313 deviation Operator_type.COMPOUND false; 314 315 deviation_list l 316 = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 317 { 318 [n, s, s2] = sum_sum2_list l; 319 } 320 321 // return n, sum, sum of squares for a list of reals 322 sum_sum2_list x 323 = foldr accumulate [0, 0, 0] x 324 { 325 accumulate x sofar 326 = [n + 1, x + s, x * x + s2], is_real x 327 = [n + n', s + s', s2 + s2'], is_list x 328 = error "sum_sum2_list: not real or [real]" 329 { 330 [n, s, s2] = sofar; 331 [n', s', s2'] = sum_sum2_list x; 332 } 333 } 334} 335 336deviationze x 337 = oo_unary_function deviationze_op x, is_class x 338 = deviationze_image x, is_image x 339 = deviationze_list x, is_real_list x || is_matrix x 340 = error (_ "bad arguments to " ++ "deviationze") 341{ 342 deviationze_op = Operator "deviationze" 343 deviationze Operator_type.COMPOUND false; 344 345 deviationze_list l 346 = (abs (s2 - (s * s / n)) / (n - 1)) ** 0.5 347 { 348 [n, s, s2] = sum_sum2_list l; 349 } 350 351 // return number of non-zero elements, sum, sum of squares for a list of 352 // reals 353 sum_sum2_list x 354 = foldr accumulate [0, 0, 0] x 355 { 356 accumulate x sofar 357 = sofar, is_real x && x == 0 358 = [n + 1, x + s, x * x + s2], is_real x 359 = [n + n', s + s', s2 + s2'], is_list x 360 = error "sum_sum2_list: not real or [real]" 361 { 362 [n, s, s2] = sofar; 363 [n', s', s2'] = sum_sum2_list x; 364 } 365 } 366 367 deviationze_image i 368 = ((sum2 - sum * sum / N) / (N - 1)) ** 0.5 369 { 370 w = get_width i; 371 h = get_height i; 372 b = get_bands i; 373 374 st = stats i; 375 sum = st.value?0?2; 376 sum2 = st.value?0?3; 377 378 // find non-zero pixels (not zero in all bands) 379 zp = im_notequal_vec i (replicate b 0); 380 381 // number of non-zero pixels 382 N = b * (mean zp * w * h) / 255; 383 } 384} 385 386// find the centre of gravity of a histogram 387gravity x 388 = oo_unary_function gravity_op x, is_class x 389 = im_hist_gravity x, is_hist x 390 = gravity_list x, is_list x 391 = error (_ "bad arguments to " ++ "gravity") 392{ 393 gravity_op = Operator "gravity" gravity Operator_type.COMPOUND false; 394 395 // centre of gravity of a histogram... use the histogram to weight an 396 // identity, then sum, then find the mean element 397 im_hist_gravity h 398 = m 399 { 400 // make horizontal 401 h' 402 = rot270 h, get_width h == 1 403 = h, get_height h == 1 404 = error "width or height not 1"; 405 406 // number of elements 407 w = get_width h'; 408 409 // matching identity 410 i 411 = im_identity_ushort 1 w, w <= 2 ** 16 - 1 412 = make_xy w 1 ? 0; 413 414 // weight identity and sum 415 s = mean (i * h') * w; 416 417 // sum of original histogram 418 s' = mean h * w; 419 420 // weighted mean 421 m = s / s'; 422 } 423 424 gravity_list l 425 = m 426 { 427 w = len l; 428 429 // matching identity 430 i = [0, 1 .. w - 1]; 431 432 // weight identity and sum 433 s = sum (map2 multiply i l); 434 435 // sum of original histogram 436 s' = sum l; 437 438 // weighted mean 439 m = s / s'; 440 } 441} 442 443project x 444 = oo_unary_function project_op x, is_class x 445 = im_project x, is_image x 446 = error (_ "bad arguments to " ++ "project") 447{ 448 project_op = Operator "project" project Operator_type.COMPOUND false; 449} 450 451abs x 452 = oo_unary_function abs_op x, is_class x 453 = im_abs x, is_image x 454 = abs_cmplx x, is_complex x 455 = abs_num x, is_real x 456 = abs_list x, is_real_list x 457 = abs_list (map abs_list x), is_matrix x 458 = error (_ "bad arguments to " ++ "abs") 459{ 460 abs_op = Operator "abs" abs Operator_type.COMPOUND false; 461 462 abs_list l = (sum (map square l)) ** 0.5; 463 464 abs_num n 465 = n, n >= 0 466 = -n; 467 468 abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; 469} 470 471copy x 472 = oo_unary_function copy_op x, is_class x 473 = im_copy x, is_image x 474 = x 475{ 476 copy_op = Operator "copy" copy Operator_type.COMPOUND_REWRAP false; 477} 478 479// like abs, but treat pixels as vectors ... ie. always get a 1-band image 480// back ... also treat matricies as lists of vectors 481// handy for dE from object difference 482abs_vec x 483 = oo_unary_function abs_vec_op x, is_class x 484 = abs_vec_image x, is_image x 485 = abs_vec_cmplx x, is_complex x 486 = abs_vec_num x, is_real x 487 = abs_vec_list x, is_real_list x 488 = mean (map abs_vec_list x), is_matrix x 489 = error (_ "bad arguments to " ++ "abs_vec") 490{ 491 abs_vec_op = Operator "abs_vec" 492 abs_vec Operator_type.COMPOUND false; 493 494 abs_vec_list l = (sum (map square l)) ** 0.5; 495 496 abs_vec_num n 497 = n, n >= 0 498 = -n; 499 500 abs_vec_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; 501 502 abs_vec_image im 503 = (sum (map square (bandsplit im))) ** 0.5; 504} 505 506transpose x 507 = oo_unary_function transpose_op x, is_class x 508 = transpose_image x, is_image x 509 = transpose_list x, is_listof is_list x 510 = error (_ "bad arguments to " ++ "transpose") 511{ 512 transpose_op = Operator "transpose" 513 transpose Operator_type.COMPOUND_REWRAP false; 514 515 transpose_list l 516 = [], l' == [] 517 = (map hd l') : (transpose_list (map tl l')) 518 { 519 l' = takewhile (not_equal []) l; 520 } 521 522 transpose_image = im_flipver @ im_rot270; 523} 524 525rot45 x 526 = oo_unary_function rot45_op x, is_class x 527 = error "rot45 image: not implemented", is_image x 528 = error (_ "bad arguments to " ++ "rot45") 529{ 530 rot45_op = Operator "rot45" 531 rot45_object Operator_type.COMPOUND_REWRAP false; 532 533 rot45_object x 534 = rot45_matrix x, is_odd_square_matrix x 535 = error "rot45 image: not implemented", is_image x 536 = error (_ "bad arguments to " ++ "rot45"); 537 538 // slow, but what the heck 539 rot45_matrix l = (im_rotate_dmask45 (Matrix l)).value; 540} 541 542// apply an image function to a [[real]] ... matrix is converted to a 1 band 543// image for processing 544apply_matrix_as_image fn m 545 = (get_value @ im_vips2mask @ fn @ im_mask2vips @ Matrix) m; 546 547// a general image/matrix operation where the mat version is most easily done 548// by converting mat->image->mat 549apply_matim_operation name fn x 550 = oo_unary_function class_op x, is_class x 551 = fn x, is_image x 552 = apply_matrix_as_image fn x, is_matrix x 553 = error (_ "bad arguments to " ++ name) 554{ 555 class_op = Operator name 556 (apply_matim_operation name fn) Operator_type.COMPOUND_REWRAP false; 557} 558 559rot90 = apply_matim_operation "rot90" im_rot90; 560rot180 = apply_matim_operation "rot180" im_rot180; 561rot270 = apply_matim_operation "rot270" im_rot270; 562rotquad = apply_matim_operation "rotquad" im_rotquad; 563fliplr = apply_matim_operation "fliplr" im_fliphor; 564fliptb = apply_matim_operation "flipud" im_flipver; 565 566image_set_type type x 567 = oo_unary_function image_set_type_op x, is_class x 568 = im_copy_set x (to_real type) 569 (get_header "Xres" x) (get_header "Yres" x) 570 (get_header "Xoffset" x) (get_header "Yoffset" x), 571 is_image x 572 = error (_ "bad arguments to " ++ "image_set_type:" ++ 573 print type ++ " " ++ print x) 574{ 575 image_set_type_op = Operator "image_set_type" 576 (image_set_type type) Operator_type.COMPOUND_REWRAP false; 577} 578 579image_set_origin xoff yoff x 580 = oo_unary_function image_set_origin_op x, is_class x 581 = im_copy_set x 582 (get_header "Type" x) 583 (get_header "Xres" x) (get_header "Yres" x) 584 (to_real xoff) (to_real yoff), 585 is_image x 586 = error (_ "bad arguments to " ++ "image_set_origin") 587{ 588 image_set_origin_op = Operator "image_set_origin" 589 (image_set_origin xoff yoff) 590 Operator_type.COMPOUND_REWRAP false; 591} 592 593cache tile_width tile_height max_tiles x 594 = oo_unary_function cache_op x, is_class x 595 = im_cache x (to_real tile_width) (to_real tile_height) 596 (to_real max_tiles), is_image x 597 = error (_ "bad arguments to " ++ "cache") 598{ 599 cache_op = Operator "cache" 600 (cache tile_width tile_height max_tiles) 601 Operator_type.COMPOUND_REWRAP false; 602} 603 604tile across down x 605 = oo_unary_function tile_op x, is_class x 606 = im_replicate x (to_real across) (to_real down), is_image x 607 = error (_ "bad arguments to " ++ "tile") 608{ 609 tile_op = Operator "tile" 610 (tile across down) Operator_type.COMPOUND_REWRAP false; 611} 612 613grid tile_height across down x 614 = oo_unary_function grid_op x, is_class x 615 = im_grid x (to_real tile_height) (to_real across) (to_real down), 616 is_image x 617 = error (_ "bad arguments to " ++ "grid") 618{ 619 grid_op = Operator "grid" 620 (grid tile_height across down) Operator_type.COMPOUND_REWRAP false; 621} 622 623max_pair a b 624 = a, a > b 625 = b; 626 627min_pair a b 628 = a, a < b 629 = b; 630 631range min value max = min_pair max (max_pair min value); 632 633max x 634 = oo_unary_function max_op x, is_class x 635 = im_max x, is_image x 636 = max_list x, is_list x 637 = x, is_number x 638 = error (_ "bad arguments to " ++ "max") 639{ 640 max_op = Operator "max" max Operator_type.COMPOUND false; 641 642 max_list x 643 = error "max []", x == [] 644 = foldr1 max_pair x, is_real_list x 645 = foldr1 max_pair (map max_list x), is_list x 646 = max x; 647} 648 649min x 650 = oo_unary_function min_op x, is_class x 651 = im_min x, is_image x 652 = min_list x, is_list x 653 = x, is_number x 654 = error (_ "bad arguments to " ++ "min") 655{ 656 min_op = Operator "min" min Operator_type.COMPOUND false; 657 658 min_list x 659 = error "min []", x == [] 660 = foldr1 min_pair x, is_real_list x 661 = foldr1 min_pair (map min_list x), is_list x 662 = min x; 663} 664 665maxpos x 666 = oo_unary_function maxpos_op x, is_class x 667 = im_maxpos x, is_image x 668 = maxpos_matrix x, is_matrix x 669 = maxpos_list x, is_list x 670 = error (_ "bad arguments to " ++ "maxpos") 671{ 672 maxpos_op = Operator "maxpos" maxpos Operator_type.COMPOUND false; 673 674 maxpos_matrix m 675 = (-1, -1), m == [[]] 676 = (indexes?row, row) 677 { 678 max_value = max (Matrix m); 679 indexes = map (index (equal max_value)) m; 680 row = index (not_equal (-1)) indexes; 681 } 682 683 maxpos_list l 684 = -1, l == [] 685 = index (equal (max l)) l; 686} 687 688minpos x 689 = oo_unary_function minpos_op x, is_class x 690 = im_minpos x, is_image x 691 = minpos_matrix x, is_matrix x 692 = minpos_list x, is_list x 693 = error (_ "bad arguments to " ++ "minpos") 694{ 695 minpos_op = Operator "minpos" minpos Operator_type.COMPOUND false; 696 697 minpos_matrix m 698 = (-1, -1), m == [[]] 699 = (indexes?row, row) 700 { 701 min_value = min (Matrix m); 702 indexes = map (index (equal min_value)) m; 703 row = index (not_equal (-1)) indexes; 704 } 705 706 minpos_list l 707 = -1, l == [] 708 = index (equal (min l)) l; 709} 710 711stats x 712 = oo_unary_function stats_op x, is_class x 713 = im_stats x, is_image x 714 = im_stats (to_image x).value, is_matrix x 715 = error (_ "bad arguments to " ++ "stats") 716{ 717 stats_op = Operator "stats" 718 stats Operator_type.COMPOUND false; 719} 720 721e = 2.7182818284590452354; 722 723pi = 3.14159265358979323846; 724 725rad d = 2 * pi * (d / 360); 726 727deg r = 360 * r / (2 * pi); 728 729sign x 730 = oo_unary_function sign_op x, is_class x 731 = im_sign x, is_image x 732 = sign_cmplx x, is_complex x 733 = sign_num x, is_real x 734 = error (_ "bad arguments to " ++ "sign") 735{ 736 sign_op = Operator "sign" sign Operator_type.COMPOUND_REWRAP false; 737 738 sign_num n 739 = 0, n == 0 740 = 1, n > 0 741 = -1; 742 743 sign_cmplx c 744 = (0, 0), mod == 0 745 = (re c / mod, im c / mod) 746 { 747 mod = abs c; 748 } 749} 750 751rint x 752 = oo_unary_function rint_op x, is_class x 753 = im_rint x, is_image x 754 = rint_value x, is_number x 755 = error (_ "bad arguments to " ++ "rint") 756{ 757 rint_op = Operator "rint" rint Operator_type.ARITHMETIC false; 758 759 rint_value x 760 = (int) (x + 0.5), x > 0 761 = (int) (x - 0.5); 762} 763 764scale x 765 = oo_unary_function scale_op x, is_class x 766 = (unsigned char) x, is_number x 767 = im_scale x, is_image x 768 = scale_list x, is_real_list x || is_matrix x 769 = error (_ "bad arguments to " ++ "scale") 770{ 771 scale_op = Operator "scale" scale Operator_type.COMPOUND_REWRAP false; 772 773 scale_list l 774 = apply_scale s o l 775 { 776 mn = find_limit min_pair l; 777 mx = find_limit max_pair l; 778 s = 255.0 / (mx - mn); 779 o = -(mn * s); 780 } 781 782 find_limit fn l 783 = find_limit fn (map (find_limit fn) l), is_listof is_list l 784 = foldr1 fn l; 785 786 apply_scale s o x 787 = x * s + o, is_number x 788 = map (apply_scale s o) x; 789} 790 791scaleps x 792 = oo_unary_function scale_op x, is_class x 793 = im_scaleps x, is_image x 794 = error (_ "bad arguments to " ++ "scale") 795{ 796 scale_op = Operator "scaleps" 797 scaleps Operator_type.COMPOUND_REWRAP false; 798} 799 800fwfft x 801 = oo_unary_function fwfft_op x, is_class x 802 = im_fwfft x, is_image x 803 = error (_ "bad arguments to " ++ "fwfft") 804{ 805 fwfft_op = Operator "fwfft" 806 fwfft Operator_type.COMPOUND_REWRAP false; 807} 808 809invfft x 810 = oo_unary_function invfft_op x, is_class x 811 = im_invfftr x, is_image x 812 = error (_ "bad arguments to " ++ "invfft") 813{ 814 invfft_op = Operator "invfft" 815 invfft Operator_type.COMPOUND_REWRAP false; 816} 817 818falsecolour x 819 = oo_unary_function falsecolour_op x, is_class x 820 = image_set_type Image_type.sRGB (im_falsecolour x), is_image x 821 = error (_ "bad arguments to " ++ "falsecolour") 822{ 823 falsecolour_op = Operator "falsecolour" 824 falsecolour Operator_type.COMPOUND_REWRAP false; 825} 826 827polar x 828 = oo_unary_function polar_op x, is_class x 829 = im_c2amph x, is_image x 830 = polar_cmplx x, is_complex x 831 = error (_ "bad arguments to " ++ "polar") 832{ 833 polar_op = Operator "polar" polar Operator_type.COMPOUND false; 834 835 polar_cmplx r 836 = (l, a) 837 { 838 a 839 = 270, x == 0 && y < 0 840 = 90, x == 0 && y >= 0 841 = 360 + atan (y / x), x > 0 && y < 0 842 = atan (y / x), x > 0 && y >= 0 843 = 180 + atan (y / x); 844 845 l = (x ** 2 + y ** 2) ** 0.5; 846 847 x = re r; 848 y = im r; 849 } 850} 851 852rectangular x 853 = oo_unary_function rectangular_op x, is_class x 854 = im_c2rect x, is_image x 855 = rectangular_cmplx x, is_complex x 856 = error (_ "bad arguments to " ++ "rectangular") 857{ 858 rectangular_op = Operator "rectangular" 859 rectangular Operator_type.COMPOUND false; 860 861 rectangular_cmplx p 862 = (x, y) 863 { 864 l = re p; 865 a = im p; 866 867 x = l * cos a; 868 y = l * sin a; 869 } 870} 871 872// we can't use colour_unary: that likes 3 band only 873recomb matrix x 874 = oo_unary_function recomb_op x, is_class x 875 = im_recomb x matrix, is_image x 876 = recomb_real_list x, is_real_list x 877 = map recomb_real_list x, is_matrix x 878 = error (_ "bad arguments to " ++ "recomb") 879{ 880 // COMPOUND_REWRAP ... signal to the colour class to go to image and 881 // back 882 recomb_op = Operator "recomb" 883 (recomb matrix) Operator_type.COMPOUND_REWRAP false; 884 885 // process [1,2,3 ..] as an image 886 recomb_real_list l 887 = (to_matrix im').value?0 888 { 889 im = (float) (to_image (Vector l)).value; 890 im' = recomb matrix im; 891 } 892} 893 894extract_area x y w h obj 895 = oo_unary_function extract_area_op obj, is_class obj 896 = im_extract_area obj x' y' w' h', is_image obj 897 = map (extract_range x' w') (extract_range y' h' obj), is_matrix obj 898 = error (_ "bad arguments to " ++ "extract_area") 899{ 900 x' = to_real x; 901 y' = to_real y; 902 w' = to_real w; 903 h' = to_real h; 904 905 extract_area_op = Operator "extract_area" (extract_area x y w h) 906 Operator_type.COMPOUND_REWRAP false; 907 908 extract_range from length list 909 = (take length @ drop from) list; 910} 911 912extract_band b obj = subscript obj b; 913 914extract_row y obj 915 = oo_unary_function extract_row_op obj, is_class obj 916 = extract_area 0 y' (get_width obj) 1 obj, is_image obj 917 = [obj?y'], is_matrix obj 918 = error (_ "bad arguments to " ++ "extract_row") 919{ 920 y' = to_real y; 921 922 extract_row_op = Operator "extract_row" (extract_row y) 923 Operator_type.COMPOUND_REWRAP false; 924} 925 926extract_column x obj 927 = oo_unary_function extract_column_op obj, is_class obj 928 = extract_area x' 0 1 height obj, is_image obj 929 = map (\row [row?x']) obj, is_matrix obj 930 = error (_ "bad arguments to " ++ "extract_column") 931{ 932 x' = to_real x; 933 height = get_header "Ysize" obj; 934 935 extract_column_op = Operator "extract_column" (extract_column x) 936 Operator_type.COMPOUND_REWRAP false; 937} 938 939blend cond in1 in2 940 = oo_binary_function blend_op cond [in1,in2], is_class cond 941 = im_blend (get_image cond) (get_image in1) (get_image in2), 942 has_image cond && has_image in1 && has_image in2 943 = error (_ "bad arguments to " ++ "blend") 944{ 945 blend_op = Operator "blend" 946 blend_obj Operator_type.COMPOUND_REWRAP false; 947 948 blend_obj cond x 949 = blend_result_image 950 { 951 [then_part, else_part] = x; 952 953 // get things about our output from inputs in this order 954 objects = [then_part, else_part, cond]; 955 956 // properties of our output image 957 target_width = get_member_list has_width get_width objects; 958 target_height = get_member_list has_height get_height objects; 959 target_bands = get_member_list has_bands get_bands objects; 960 target_format = get_member_list has_format get_format objects; 961 target_type = get_member_list has_type get_type objects; 962 963 to_image x 964 = to_image_size target_width target_height 965 target_bands target_format x; 966 967 [then_image, else_image] = map to_image [then_part, else_part]; 968 969 blend_result_image = image_set_type target_type 970 (im_blend cond then_image else_image); 971 } 972} 973 974// do big first: we want to keep big's class, if possible 975// eg. big is a Plot, small is a 1x1 Image 976insert x y small big 977 = oo_binary'_function insert_op small big, is_class big 978 = oo_binary_function insert_op small big, is_class small 979 = im_insert big small (to_real x) (to_real y), 980 is_image small && is_image big 981 = error (_ "bad arguments to " ++ "insert") 982{ 983 insert_op = Operator "insert" 984 (insert x y) Operator_type.COMPOUND_REWRAP false; 985} 986 987insert_noexpand x y small big 988 = oo_binary_function insert_noexpand_op small big, is_class small 989 = oo_binary'_function insert_noexpand_op small big, is_class big 990 = im_insert_noexpand big small (to_real x) (to_real y), 991 is_image small && is_image big 992 = error (_ "bad arguments to " ++ "insert_noexpand") 993{ 994 insert_noexpand_op = Operator "insert_noexpand" 995 (insert_noexpand x y) Operator_type.COMPOUND_REWRAP false; 996} 997 998measure x y w h u v image 999 = oo_unary_function measure_op image, is_class image 1000 = im_measure image 1001 (to_real x) (to_real y) (to_real w) (to_real h) 1002 (to_real u) (to_real v), 1003 is_image image 1004 = error (_ "bad arguments to " ++ "measure") 1005{ 1006 measure_op = Operator "measure" 1007 (measure x y w h u v) Operator_type.COMPOUND_REWRAP false; 1008} 1009 1010extract_bands b n obj 1011 = oo_unary_function extract_bands_op obj, is_class obj 1012 = im_extract_bands obj (to_real b) (to_real n), is_image obj 1013 = error (_ "bad arguments to " ++ "extract_bands") 1014{ 1015 extract_bands_op = Operator "extract_bands" 1016 (extract_bands b n) Operator_type.COMPOUND_REWRAP false; 1017} 1018 1019invert x 1020 = oo_unary_function invert_op x, is_class x 1021 = im_invert x, is_image x 1022 = 255 - x, is_real x 1023 = error (_ "bad arguments to " ++ "invert") 1024{ 1025 invert_op = Operator "invert" invert Operator_type.COMPOUND false; 1026} 1027 1028transform ipol wrap params image 1029 = oo_unary_function transform_op image, is_class image 1030 = im_transform image 1031 (to_matrix params) (to_real ipol) (to_real wrap), is_image image 1032 = error (_ "bad arguments to " ++ "transform") 1033{ 1034 transform_op = Operator "transform" 1035 (transform ipol wrap params) 1036 Operator_type.COMPOUND_REWRAP false; 1037} 1038 1039transform_search max_error max_iterations order ipol wrap sample reference 1040 = oo_binary_function transform_search_op sample reference, is_class sample 1041 = oo_binary'_function transform_search_op sample reference, 1042 is_class reference 1043 = im_transform_search sample reference 1044 (to_real max_error) (to_real max_iterations) (to_real order) 1045 (to_real ipol) (to_real wrap), 1046 is_image sample && is_image reference 1047 = error (_ "bad arguments to " ++ "transform_search") 1048{ 1049 transform_search_op = Operator "transform_search" 1050 (transform_search max_error max_iterations order ipol wrap) 1051 Operator_type.COMPOUND false; 1052} 1053 1054rotate interp angle image 1055 = oo_binary_function rotate_op angle image, is_class angle 1056 = oo_binary'_function rotate_op angle image, is_class image 1057 = im_affinei_all image interp.value a (-b) b a 0 0, 1058 is_real angle && is_image image 1059 = error (_ "bad arguments to " ++ "rotate") 1060{ 1061 rotate_op = Operator "rotate" 1062 (rotate interp) Operator_type.COMPOUND_REWRAP false; 1063 a = cos angle; 1064 b = sin angle; 1065} 1066 1067matrix_binary fn a b 1068 = itom (fn (mtoi a) (mtoi b)) 1069{ 1070 mtoi x = im_mask2vips (Matrix x); 1071 itom x = (im_vips2mask x).value; 1072} 1073 1074join_lr a b 1075 = oo_binary_function join_lr_op a b, is_class a 1076 = oo_binary'_function join_lr_op a b, is_class b 1077 = join_im a b, is_image a && is_image b 1078 = matrix_binary join_im a b, is_matrix a && is_matrix b 1079 = error (_ "bad arguments to " ++ "join_lr") 1080{ 1081 join_lr_op = Operator "join_lr" 1082 join_lr Operator_type.COMPOUND_REWRAP false; 1083 1084 join_im a b = insert (get_width a) 0 b a; 1085} 1086 1087join_tb a b 1088 = oo_binary_function join_tb_op a b, is_class a 1089 = oo_binary'_function join_tb_op a b, is_class b 1090 = join_im a b, is_image a && is_image b 1091 = matrix_binary join_im a b, is_matrix a && is_matrix b 1092 = error (_ "bad arguments to " ++ "join_tb") 1093{ 1094 join_tb_op = Operator "join_tb" 1095 join_tb Operator_type.COMPOUND_REWRAP false; 1096 1097 join_im a b = insert 0 (get_height a) b a; 1098} 1099 1100conj x 1101 = oo_unary_function conj_op x, is_class x 1102 = (re x, -im x), 1103 is_complex x || 1104 (is_image x && format == Image_format.COMPLEX) || 1105 (is_image x && format == Image_format.DPCOMPLEX) 1106 // assume it's some sort of real 1107 = x 1108{ 1109 format = get_header "BandFmt" x; 1110 conj_op = Operator "conj" conj Operator_type.COMPOUND false; 1111} 1112 1113clip2fmt format image 1114 = oo_unary_function clip2fmt_op image, is_class image 1115 = im_clip2fmt image (to_real format), is_image image 1116 = error (_ "bad arguments to " ++ "clip2fmt") 1117{ 1118 clip2fmt_op = Operator "clip2fmt" 1119 (clip2fmt format) Operator_type.COMPOUND_REWRAP false; 1120} 1121 1122embed type x y w h im 1123 = oo_unary_function embed_op im, is_class im 1124 = im_embed im (to_real type) 1125 (to_real x) (to_real y) (to_real w) (to_real h), is_image im 1126 = error (_ "bad arguments to " ++ "embed") 1127{ 1128 embed_op = Operator "embed" 1129 (embed type x y w h) Operator_type.COMPOUND_REWRAP false; 1130} 1131 1132/* Morph a mask with a [[real]] matrix ... turn m2 into an image, morph it 1133 * with m1, turn it back to a matrix again. 1134 */ 1135_morph_2_masks fn m1 m2 1136 = m'' 1137{ 1138 // The [[real]] can contain 128 values ... squeeze them out! 1139 image = im_mask2vips (Matrix m2) == 255; 1140 m2_width = get_width image; 1141 m2_height = get_height image; 1142 1143 // need to embed m2 in an image large enough for us to be able to 1144 // position m1 all around the edges, with a 1 pixel overlap 1145 image' = embed 0 1146 (m1.width / 2) (m1.height / 2) 1147 (m2_width + (m1.width - 1)) (m2_height + (m1.height - 1)) 1148 image; 1149 1150 // morph! 1151 image'' = fn m1 image'; 1152 1153 // back to mask 1154 m' = im_vips2mask ((double) image''); 1155 1156 // Turn 0 in output to 128 (don't care). 1157 m'' 1158 = map (map fn) m'.value 1159 { 1160 fn a 1161 = 128, a == 0; 1162 = a; 1163 } 1164} 1165 1166dilate mask image 1167 = oo_unary_function dilate_op image, is_class image 1168 = im_dilate image (to_matrix mask), is_image image 1169 = error (_ "bad arguments to " ++ "dilate") 1170{ 1171 dilate_op = Operator "dilate" 1172 dilate_object Operator_type.COMPOUND_REWRAP false; 1173 1174 dilate_object x 1175 = _morph_2_masks dilate mask x, is_matrix x 1176 = dilate mask x; 1177} 1178 1179erode mask image 1180 = oo_unary_function erode_op image, is_class image 1181 = im_erode image (to_matrix mask), is_image image 1182 = error (_ "bad arguments to " ++ "erode") 1183{ 1184 erode_op = Operator "erode" 1185 erode_object Operator_type.COMPOUND_REWRAP false; 1186 1187 erode_object x 1188 = _morph_2_masks erode mask x, is_matrix x 1189 = erode mask x; 1190} 1191 1192conv mask image 1193 = oo_unary_function conv_op image, is_class image 1194 = im_conv image (to_matrix mask), is_image image 1195 = error (_ "bad arguments to " ++ "conv" ++ ": " ++ 1196 "conv (" ++ print mask ++ ") (" ++ print image ++ ")") 1197{ 1198 conv_op = Operator "conv" 1199 (conv mask) Operator_type.COMPOUND_REWRAP false; 1200} 1201 1202convf mask image 1203 = oo_unary_function convf_op image, is_class image 1204 = im_conv_f image (to_matrix mask), is_image image 1205 = error (_ "bad arguments to " ++ "convf" ++ ": " ++ 1206 "convf (" ++ print mask ++ ") (" ++ print image ++ ")") 1207{ 1208 convf_op = Operator "convf" 1209 (convf mask) Operator_type.COMPOUND_REWRAP false; 1210} 1211 1212convsep mask image 1213 = oo_unary_function convsep_op image, is_class image 1214 = im_convsep image (to_matrix mask), is_image image 1215 = error (_ "bad arguments to " ++ "convsep") 1216{ 1217 convsep_op = Operator "convsep" 1218 (convsep mask) Operator_type.COMPOUND_REWRAP false; 1219} 1220 1221aconvsep layers mask image 1222 = oo_unary_function aconvsep_op image, is_class image 1223 = im_aconvsep image (to_matrix mask) (to_real layers), is_image image 1224 = error (_ "bad arguments to " ++ "aconvsep") 1225{ 1226 aconvsep_op = Operator "aconvsep" 1227 (aconvsep layers mask) Operator_type.COMPOUND_REWRAP false; 1228} 1229 1230convsepf mask image 1231 = oo_unary_function convsepf_op image, is_class image 1232 = im_convsep_f image (to_matrix mask), is_image image 1233 = error (_ "bad arguments to " ++ "convsepf") 1234{ 1235 convsepf_op = Operator "convsepf" 1236 (convsepf mask) Operator_type.COMPOUND_REWRAP false; 1237} 1238 1239rank w h n image 1240 = oo_unary_function rank_op image, is_class image 1241 = im_rank image (to_real w) (to_real h) (to_real n), is_image image 1242 = error (_ "bad arguments to " ++ "rank") 1243{ 1244 rank_op = Operator "rank" 1245 (rank w h n) Operator_type.COMPOUND_REWRAP false; 1246} 1247 1248rank_image n x 1249 = rlist x.value, is_Group x 1250 = rlist x, is_list x 1251 = error (_ "bad arguments to " ++ "rank_image") 1252{ 1253 rlist l 1254 = wrapper ranked, has_wrapper 1255 = ranked 1256 { 1257 has_wrapper = has_member_list (has_member "Image") l; 1258 wrapper = get_member_list (has_member "Image") (get_member "Image") l; 1259 ranked = im_rank_image (map get_image l) (to_real n); 1260 } 1261} 1262 1263greyc iterations amplitude sharpness anisotropy alpha 1264 sigma dl da gauss_prec interpolation fast_approx x 1265 = oo_unary_function greyc_op x, is_class x 1266 = greyc_im x, is_image x 1267 = error (_ "bad argument" ++ " (" ++ print x ++ ") to " ++ "greyc") 1268{ 1269 greyc_op = Operator "greyc" (greyc 1270 iterations amplitude sharpness anisotropy alpha 1271 sigma dl da gauss_prec interpolation fast_approx) 1272 Operator_type.COMPOUND_REWRAP false; 1273 greyc_im x = im_greyc x 1274 iterations amplitude sharpness anisotropy alpha 1275 sigma dl da gauss_prec interpolation fast_approx; 1276} 1277 1278greyc_mask iterations amplitude sharpness anisotropy alpha 1279 sigma dl da gauss_prec interpolation fast_approx mask x 1280 = oo_binary_function greyc_mask_op mask x, is_class mask 1281 = oo_binary'_function greyc_mask_op mask x, is_class x 1282 = greyc_im mask x, is_image mask && is_image x 1283 = error (_ "bad arguments" ++ 1284 " (" ++ print mask ++ ", " ++ print x ++ ") " ++ 1285 "to " ++ "greyc") 1286{ 1287 greyc_mask_op = Operator "greyc_mask" (greyc_mask 1288 iterations amplitude sharpness anisotropy alpha 1289 sigma dl da gauss_prec interpolation fast_approx) 1290 Operator_type.COMPOUND_REWRAP false; 1291 greyc_im mask x = im_greyc_mask x mask 1292 iterations amplitude sharpness anisotropy alpha 1293 sigma dl da gauss_prec interpolation fast_approx; 1294} 1295 1296// find the correlation surface for a small image within a big one 1297correlate small big 1298 = oo_binary_function correlate_op small big, is_class small 1299 = oo_binary'_function correlate_op small big, is_class big 1300 = im_spcor big small, is_image small && is_image big 1301 = error (_ "bad arguments to " ++ "correlate") 1302{ 1303 correlate_op = Operator "correlate" 1304 correlate Operator_type.COMPOUND_REWRAP false; 1305} 1306 1307// just sum-of-squares-of-differences 1308correlate_fast small big 1309 = oo_binary_function correlate_fast_op small big, is_class small 1310 = oo_binary'_function correlate_fast_op small big, is_class big 1311 = im_fastcor big small, is_image small && is_image big 1312 = error (_ "bad arguments to " ++ "correlate_fast") 1313{ 1314 correlate_fast_op = Operator "correlate_fast" 1315 correlate_fast Operator_type.COMPOUND_REWRAP false; 1316} 1317 1318// set Type, wrap as Plot_hist if it's a class 1319hist_tag x 1320 = oo_unary_function hist_tag_op x, is_class x 1321 = image_set_type Image_type.HISTOGRAM x, is_image x 1322 = error (_ "bad arguments to " ++ "hist_tag") 1323{ 1324 hist_tag_op = Operator "hist_tag" 1325 (Plot_histogram @ hist_tag) Operator_type.COMPOUND false; 1326} 1327 1328hist_find x 1329 = oo_unary_function hist_find_op x, is_class x 1330 = im_histgr x (-1), is_image x 1331 = error (_ "bad arguments to " ++ "hist_find") 1332{ 1333 hist_find_op = Operator "hist_find" 1334 (Plot_histogram @ hist_find) Operator_type.COMPOUND false; 1335} 1336 1337hist_find_nD bins image 1338 = oo_unary_function hist_find_nD_op image, is_class image 1339 = im_histnD image (to_real bins), is_image image 1340 = error (_ "bad arguments to " ++ "hist_find_nD") 1341{ 1342 hist_find_nD_op = Operator "hist_find_nD" 1343 (hist_find_nD bins) Operator_type.COMPOUND_REWRAP false; 1344} 1345 1346hist_find_indexed index value 1347 = oo_binary_function hist_find_indexed_op index value, is_class index 1348 = oo_binary'_function hist_find_indexed_op index value, is_class value 1349 = im_hist_indexed index value, is_image index && is_image value 1350 = error (_ "bad arguments to " ++ "hist_find_indexed") 1351{ 1352 hist_find_indexed_op = Operator "hist_find_indexed" 1353 (compose (compose Plot_histogram) hist_find_indexed) 1354 Operator_type.COMPOUND false; 1355} 1356 1357hist_map hist image 1358 = oo_binary_function hist_map_op hist image, is_class hist 1359 = oo_binary'_function hist_map_op hist image, is_class image 1360 = im_maplut image hist, is_image hist && is_image image 1361 = error (_ "bad arguments to " ++ "hist_map") 1362{ 1363 // can't use rewrap, as we want to always wrap as image 1364 hist_map_op = Operator "hist_map" 1365 (compose (compose Image) hist_map) Operator_type.COMPOUND false; 1366} 1367 1368hist_cum hist 1369 = oo_unary_function hist_cum_op hist, is_class hist 1370 = im_histcum hist, is_image hist 1371 = error (_ "bad arguments to " ++ "hist_cum") 1372{ 1373 hist_cum_op = Operator "hist_cum" 1374 hist_cum Operator_type.COMPOUND_REWRAP false; 1375} 1376 1377hist_diff hist 1378 = oo_unary_function hist_diff_op hist, is_class hist 1379 = im_histdiff hist, is_image hist 1380 = error (_ "bad arguments to " ++ "hist_diff") 1381{ 1382 hist_diff_op = Operator "hist_diff" 1383 hist_diff Operator_type.COMPOUND_REWRAP false; 1384 1385 im_histdiff h 1386 = (conv (Matrix [[-1, 1]]) @ clip2fmt (fmt (get_format h))) h 1387 { 1388 // up the format so it can represent the whole range of 1389 // possible values from this mask 1390 fmt x 1391 = Image_format.SHORT, 1392 x == Image_format.UCHAR || x == Image_format.CHAR 1393 = Image_format.INT, 1394 x == Image_format.USHORT || x == Image_format.SHORT || 1395 x == Image_format.UINT 1396 = x; 1397 } 1398} 1399 1400hist_norm hist 1401 = oo_unary_function hist_norm_op hist, is_class hist 1402 = im_histnorm hist, is_image hist 1403 = error (_ "bad arguments to " ++ "hist_norm") 1404{ 1405 hist_norm_op = Operator "hist_norm" 1406 hist_norm Operator_type.COMPOUND_REWRAP false; 1407} 1408 1409hist_match in ref 1410 = oo_binary_function hist_match_op in ref, is_class in 1411 = oo_binary'_function hist_match_op in ref, is_class ref 1412 = im_histspec in ref, is_image in && is_image ref 1413 = error (_ "bad arguments to " ++ "hist_match") 1414{ 1415 hist_match_op = Operator "hist_match" 1416 hist_match Operator_type.COMPOUND_REWRAP false; 1417} 1418 1419hist_equalize x = hist_map ((hist_norm @ hist_cum @ hist_find) x) x; 1420 1421hist_equalize_local w h image 1422 = oo_unary_function hist_equalize_local_op image, is_class image 1423 = lhisteq image, is_image image 1424 = error (_ "bad arguments to " ++ "hist_equalize_local") 1425{ 1426 hist_equalize_local_op = Operator "hist_equalize_local" 1427 (hist_equalize_local w h) Operator_type.COMPOUND_REWRAP false; 1428 1429 // loop over bands, if necessary 1430 lhisteq im 1431 = im_lhisteq im (to_real w) (to_real h), get_bands im == 1 1432 = (foldl1 join @ map lhisteq @ bandsplit) im; 1433} 1434 1435// find the threshold below which are percent of the image (percent in [0,1]) 1436// eg. hist_thresh 0.1 x == 12, then x < 12 will light up 10% of the pixels 1437hist_thresh percent image 1438 = x 1439{ 1440 // our own normaliser ... we don't want to norm channels separately 1441 // norm to [0,1] 1442 my_hist_norm h = h / max h; 1443 1444 // normalised cumulative hist 1445 // we sum the channels before we normalise, because we want to treat them 1446 // all the same 1447 h = (my_hist_norm @ sum @ bandsplit @ hist_cum @ hist_find) 1448 image.value; 1449 1450 // threshold that, then use im_profile to search for the x position in the 1451 // histogram 1452 x = mean (im_profile (h > percent) 1); 1453} 1454 1455/* Sometimes useful, despite plotting now being built in, for making 1456 * diagnostic images. 1457 */ 1458hist_plot hist 1459 = oo_unary_function hist_plot_op hist, is_class hist 1460 = im_histplot hist, is_image hist 1461 = error (_ "bad arguments to " ++ "hist_plot") 1462{ 1463 hist_plot_op = Operator "hist_plot" 1464 (Image @ hist_plot) Operator_type.COMPOUND false; 1465} 1466 1467zerox d x 1468 = oo_unary_function zerox_op x, is_class x 1469 = im_zerox x (to_real d), is_image x 1470 = error (_ "bad arguments to " ++ "zerox") 1471{ 1472 zerox_op = Operator "zerox" 1473 (zerox d) Operator_type.COMPOUND_REWRAP false; 1474} 1475 1476resize interp xfac yfac image 1477 = oo_unary_function resize_op image, is_class image 1478 = resize_im image, is_image image 1479 = error (_ "bad arguments to " ++ "resize") 1480{ 1481 resize_op = Operator "resize" 1482 resize_im Operator_type.COMPOUND_REWRAP false; 1483 1484 xfac' = to_real xfac; 1485 yfac' = to_real yfac; 1486 1487 rxfac' = 1 / xfac'; 1488 ryfac' = 1 / yfac'; 1489 1490 // is this interpolation nearest-neighbour? 1491 is_nn x = x.type == Interpolate_type.NEAREST_NEIGHBOUR; 1492 1493 resize_im im 1494 // upscale by integer factor, nearest neighbour 1495 = im_zoom im xfac' yfac', 1496 is_int xfac' && is_int yfac' && 1497 xfac' >= 1 && yfac' >= 1 && 1498 is_nn interp 1499 1500 // downscale by integer factor, nearest neighbour 1501 = im_subsample im rxfac' ryfac', 1502 is_int rxfac' && is_int ryfac' && 1503 rxfac' >= 1 && ryfac' >= 1 && 1504 is_nn interp 1505 1506 // upscale by any factor, nearest neighbour 1507 // upscale by integer part, then affine to exact size 1508 = scale xg?1 yg?1 (im_zoom im xg?0 yg?0), 1509 xfac' >= 1 && yfac' >= 1 && 1510 is_nn interp 1511 1512 // downscale by any factor, nearest neighbour 1513 // downscale by integer part, then affine to exact size 1514 = scale xs?1 ys?1 (im_subsample im xs?0 ys?0), 1515 rxfac' >= 1 && ryfac' >= 1 && 1516 is_nn interp 1517 1518 // upscale by any factor with affine 1519 = scale xfac' yfac' im, 1520 xfac' >= 1 && yfac' >= 1 1521 1522 // downscale by any factor, bilinear 1523 // block shrink by integer factor, then resample to 1524 // exact with affine 1525 = scale xs?1 ys?1 (im_shrink im xs?0 ys?0), 1526 rxfac' >= 1 && ryfac' >= 1 1527 1528 = error ("resize: unimplemented argument combination:\n" ++ 1529 " xfac = " ++ print xfac' ++ "\n" ++ 1530 " yfac = " ++ print yfac' ++ "\n" ++ 1531 " interp = " ++ print interp ++ " (" ++ 1532 Interpolate_type.descriptions?interp.type ++ ")") 1533 { 1534 // convert a float scale to integer plus fraction 1535 // eg. scale by 2.5 becomes [2, 1.25] (x * 2.5 == x * 2 * 1.25) 1536 break f = [floor f, f / floor f]; 1537 1538 // same, but for downsizing ... turn a float scale which is less than 1539 // 1 into an int shrink and a float scale 1540 1541 // complicated: the int shrink may round the size down (eg. imagine 1542 // subsampling a 11 pixel wide image by 3, we'd get a 3 pixel wide 1543 // image, not a 3.666 pixel wide image), so pass in the size of image 1544 // we are operating on and adjust for any rounding 1545 1546 // so ... x is (eg.) 467, f is (eg. 128/467, about 0.274) 1547 rbreak x f 1548 = [int_shrink, float_resample] 1549 { 1550 // the size of image we are aiming for after the combined int and 1551 // float resample 1552 x' = x * f; 1553 1554 // int part 1555 int_shrink = floor (1 / f); 1556 1557 // size after int shrink 1558 x'' = floor (x / int_shrink); 1559 1560 // therefore what we need for the float part 1561 float_resample = x' / x''; 1562 } 1563 1564 width = get_width im; 1565 height = get_height im; 1566 1567 // grow and shrink factors 1568 xg = break xfac'; 1569 yg = break yfac'; 1570 xs = rbreak width xfac'; 1571 ys = rbreak height yfac'; 1572 1573 // resize 1574 scale xfac yfac im 1575 = im_affinei_all im interp.value xfac 0 0 yfac 0 0; 1576 } 1577} 1578 1579sharpen radius x1 y2 y3 m1 m2 in 1580 = oo_unary_function sharpen_op in, is_class in 1581 = im_sharpen in (to_real radius) 1582 (to_real x1) (to_real y2) (to_real y3) 1583 (to_real m1) (to_real m2), is_image in 1584 = error (_ "bad arguments to " ++ "sharpen") 1585{ 1586 sharpen_op = Operator "sharpen" 1587 (sharpen radius x1 y2 y3 m1 m2) 1588 Operator_type.COMPOUND_REWRAP false; 1589} 1590 1591tone_analyse s m h sa ma ha in 1592 = oo_unary_function tone_analyse_op in, is_class in 1593 = im_tone_analyse in 1594 (to_real s) (to_real m) (to_real h) 1595 (to_real sa) (to_real ma) (to_real ha), is_image in 1596 = error (_ "bad arguments to " ++ "tone_analyse") 1597{ 1598 tone_analyse_op = Operator "tone_analyse" 1599 (Plot_histogram @ tone_analyse s m h sa ma ha) 1600 Operator_type.COMPOUND false; 1601} 1602 1603tone_map hist image 1604 = oo_binary_function tone_map_op hist image, is_class hist 1605 = oo_binary'_function tone_map_op hist image, is_class image 1606 = im_tone_map image hist, is_image hist && is_image image 1607 = error (_ "bad arguments to " ++ "tone_map") 1608{ 1609 tone_map_op = Operator "tone_map" 1610 tone_map Operator_type.COMPOUND_REWRAP false; 1611} 1612 1613tone_build fmt b w s m h sa ma ha 1614 = (Plot_histogram @ clip2fmt fmt) 1615 (im_tone_build_range mx mx 1616 (to_real b) (to_real w) 1617 (to_real s) (to_real m) (to_real h) 1618 (to_real sa) (to_real ma) (to_real ha)) 1619{ 1620 mx = Image_format.maxval fmt; 1621} 1622 1623icc_export depth profile intent in 1624 = oo_unary_function icc_export_op in, is_class in 1625 = im_icc_export_depth in 1626 (to_real depth) (expand profile) (to_real intent), is_image in 1627 = error (_ "bad arguments to " ++ "icc_export") 1628{ 1629 icc_export_op = Operator "icc_export" 1630 (icc_export depth profile intent) 1631 Operator_type.COMPOUND_REWRAP false; 1632} 1633 1634icc_import profile intent in 1635 = oo_unary_function icc_import_op in, is_class in 1636 = im_icc_import in 1637 (expand profile) (to_real intent), is_image in 1638 = error (_ "bad arguments to " ++ "icc_import") 1639{ 1640 icc_import_op = Operator "icc_import" 1641 (icc_import profile intent) 1642 Operator_type.COMPOUND_REWRAP false; 1643} 1644 1645icc_import_embedded intent in 1646 = oo_unary_function icc_import_embedded_op in, is_class in 1647 = im_icc_import_embedded in (to_real intent), is_image in 1648 = error (_ "bad arguments to " ++ "icc_import_embedded") 1649{ 1650 icc_import_embedded_op = Operator "icc_import_embedded" 1651 (icc_import_embedded intent) 1652 Operator_type.COMPOUND_REWRAP false; 1653} 1654 1655icc_transform in_profile out_profile intent in 1656 = oo_unary_function icc_transform_op in, is_class in 1657 = im_icc_transform in 1658 (expand in_profile) (expand out_profile) 1659 (to_real intent), is_image in 1660 = error (_ "bad arguments to " ++ "icc_transform") 1661{ 1662 icc_transform_op = Operator "icc_transform" 1663 (icc_transform in_profile out_profile intent) 1664 Operator_type.COMPOUND_REWRAP false; 1665} 1666 1667icc_ac2rc profile in 1668 = oo_unary_function icc_ac2rc_op in, is_class in 1669 = im_icc_ac2rc in (expand profile), is_image in 1670 = error (_ "bad arguments to " ++ "icc_ac2rc") 1671{ 1672 icc_ac2rc_op = Operator "icc_ac2rc" 1673 (icc_ac2rc profile) 1674 Operator_type.COMPOUND_REWRAP false; 1675} 1676 1677draw_flood_blob x y ink image 1678 = oo_unary_function draw_flood_blob_op image, is_class image 1679 = im_draw_flood_blob image (to_real x) (to_real y) ink, is_image image 1680 = error (_ "bad arguments to " ++ "draw_flood_blob") 1681{ 1682 draw_flood_blob_op = Operator "draw_flood_blob" 1683 (draw_flood_blob x y ink) Operator_type.COMPOUND_REWRAP false; 1684} 1685 1686draw_flood x y ink image 1687 = oo_unary_function draw_flood_op image, is_class image 1688 = im_draw_flood image (to_real x) (to_real y) ink, is_image image 1689 = error (_ "bad arguments to " ++ "draw_flood") 1690{ 1691 draw_flood_op = Operator "draw_flood" 1692 (draw_flood x y ink) Operator_type.COMPOUND_REWRAP false; 1693} 1694 1695draw_rect x y w h f ink image 1696 = oo_unary_function draw_rect_op image, is_class image 1697 = im_draw_rect image (to_real x) (to_real y) 1698 (to_real w) (to_real h) (to_real f) ink, is_image image 1699 = error (_ "bad arguments to " ++ "draw_rect") 1700{ 1701 draw_rect_op = Operator "draw_rect" 1702 (draw_rect x y w h f ink) Operator_type.COMPOUND_REWRAP false; 1703} 1704 1705draw_circle x y r f ink image 1706 = oo_unary_function draw_circle_op image, is_class image 1707 = im_draw_circle image (to_real x) (to_real y) 1708 (to_real r) (to_real f) ink, is_image image 1709 = error (_ "bad arguments to " ++ "draw_circle") 1710{ 1711 draw_circle_op = Operator "draw_circle" 1712 (draw_circle x y r f ink) Operator_type.COMPOUND_REWRAP false; 1713} 1714 1715draw_line x1 y1 x2 y2 ink image 1716 = oo_unary_function draw_line_op image, is_class image 1717 = im_draw_line image (to_real x1) (to_real y1) 1718 (to_real x2) (to_real y2) ink, is_image image 1719 = error (_ "bad arguments to " ++ "draw_line") 1720{ 1721 draw_line_op = Operator "draw_line" 1722 (draw_line x1 y1 x2 y2 ink) Operator_type.COMPOUND_REWRAP false; 1723} 1724 1725print_base base in 1726 = oo_unary_function print_base_op in, is_class in 1727 = map (print_base base) in, is_list in 1728 = print_base_real, is_real in 1729 = error (_ "bad arguments to " ++ "print_base") 1730{ 1731 print_base_op 1732 = Operator "print_base" (print_base base) Operator_type.COMPOUND false; 1733 1734 print_base_real 1735 = error "print_base: bad base", base < 2 || base > 16 1736 = "0", in < 0 || chars == [] 1737 = reverse chars 1738 { 1739 digits = map (\x x % base) 1740 (takewhile (not_equal 0) 1741 (iterate (\x idivide x base) in)); 1742 chars = map tohd digits; 1743 1744 tohd x 1745 = (char) ((int) '0' + x), x < 10 1746 = (char) ((int) 'A' + (x - 10)); 1747 } 1748} 1749 1750/* id x: the identity function 1751 * 1752 * id :: * -> * 1753 */ 1754id x = x; 1755 1756/* const x y: junk y, return x 1757 * 1758 * (const 3) is the function that always returns 3. 1759 * const :: * -> ** -> * 1760 */ 1761const x y = x; 1762 1763/* converse fn a b: swap order of args to fn 1764 * 1765 * converse fn a b == fn b a 1766 * converse :: (* -> ** -> ***) -> ** -> * -> *** 1767 */ 1768converse fn a b = fn b a; 1769 1770/* fix fn x: find the fixed point of a function 1771 */ 1772fix fn x = limit (iterate fn x); 1773 1774/* until pred fn n: apply fn to n until pred succeeds; return that value 1775 * 1776 * until (more 1000) (multiply 2) 1 = 1024 1777 * until :: (* -> bool) -> (* -> *) -> * -> * 1778 */ 1779until pred fn n 1780 = n, pred n 1781 = until pred fn (fn n); 1782 1783/* Infinite list of primes. 1784 */ 1785primes 1786 = 1 : (sieve [2 ..]) 1787{ 1788 sieve l = hd l : sieve (filter (nmultiple (hd l)) (tl l)); 1789 nmultiple n x = x / n != (int) (x / n); 1790} 1791 1792/* Map an n-ary function (pass the args as a list) over groups of objects. 1793 * The objects can be single objects or groups. If more than one 1794 * object is a group, we iterate for the length of the smallest group. 1795 * Don't loop over plain lists, since we want (eg.) "mean [1, 2, 3]" to work. 1796 * Treat [] as no-value --- ie. if any of the inputs are [] we put [] into the 1797 * output and don't apply the function. 1798 1799 copy-pasted into _types, keep in sync 1800 1801 */ 1802map_nary fn args 1803 = fn args, groups == [] 1804 = Group (map process [0, 1 .. shortest - 1]) 1805{ 1806 // find all the group arguments 1807 groups = filter is_Group args; 1808 1809 // what's the length of the shortest group arg? 1810 shortest = foldr1 min_pair (map (len @ get_value) groups); 1811 1812 // process index n ... pull that member from each argument 1813 // recurse to handle application, so we work for nested groups too 1814 process n 1815 = NULL, any (map (is_noval n) args) 1816 = map_nary fn (map (extract n) args) 1817 { 1818 extract n arg 1819 = arg.value?n, is_Group arg 1820 = arg; 1821 1822 is_noval n arg = is_Group arg && arg.value?n == NULL; 1823 } 1824} 1825 1826/* Map a 1-ary function over an object. 1827 */ 1828map_unary fn a = map_nary (list_1ary fn) [a]; 1829 1830/* Map a 2-ary function over a pair of objects. 1831 */ 1832map_binary fn a b = map_nary (list_2ary fn) [a, b]; 1833 1834/* Map a 3-ary function over three objects. 1835 */ 1836map_trinary fn a b c = map_nary (list_3ary fn) [a, b, c]; 1837 1838/* Map a 4-ary function over three objects. 1839 */ 1840map_quaternary fn a b c d = map_nary (list_4ary fn) [a, b, c, d]; 1841 1842/* Same as map_nary, but for lists. Handy for (eg.) implementing arith ops on 1843 * vectors and matricies. 1844 */ 1845map_nary_list fn args 1846 = fn args, lists == [] 1847 = map process [0, 1 .. shortest - 1] 1848{ 1849 // find all the list arguments 1850 lists = filter is_list args; 1851 1852 // what's the length of the shortest list arg? 1853 shortest = foldr1 min_pair (map len lists); 1854 1855 // process index n ... pull that member from each argument 1856 // recurse to handle application, so we work for nested lists too 1857 process n 1858 = map_nary_list fn (map (extract n) args) 1859 { 1860 extract n arg 1861 = arg?n, is_list arg 1862 = arg; 1863 } 1864} 1865 1866map_unaryl fn a = map_nary_list (list_1ary fn) [a]; 1867 1868map_binaryl fn a b = map_nary_list (list_2ary fn) [a, b]; 1869 1870/* Remove features smaller than x pixels across from an image. This used to be 1871 * rather complex ... convsep is now good enough to use. 1872 */ 1873smooth x image = convsep (matrix_gaussian_blur (to_real x * 2)) image; 1874 1875/* Chop up an image into a list of lists of smaller images. Pad edges with 1876 * black. 1877 */ 1878imagearray_chop tile_width tile_height hoverlap voverlap i 1879 = map chop' [0, vstep .. last_y] 1880{ 1881 width = get_width i; 1882 height = get_height i; 1883 bands = get_bands i; 1884 format = get_format i; 1885 type = get_type i; 1886 1887 tile_width' = to_real tile_width; 1888 tile_height' = to_real tile_height; 1889 hoverlap' = to_real hoverlap; 1890 voverlap' = to_real voverlap; 1891 1892 /* Unique pixels per tile. 1893 */ 1894 hstep = tile_width' - hoverlap'; 1895 vstep = tile_height' - voverlap'; 1896 1897 /* Number of tiles across and down. Remember the case where width == 1898 * hstep. 1899 */ 1900 across = (int) ((width - 1) / hstep) + 1; 1901 down = (int) ((height - 1) / vstep) + 1; 1902 1903 /* top/left of final tile. 1904 */ 1905 last_x = hstep * (across - 1); 1906 last_y = vstep * (down - 1); 1907 1908 /* How much do we need to pad by? 1909 */ 1910 sx = last_x + tile_width'; 1911 sy = last_y + tile_height'; 1912 1913 /* Expand image with black to pad size. 1914 */ 1915 pad = embed 0 0 0 sx sy i; 1916 1917 /* Chop up a row. 1918 */ 1919 chop' y 1920 = map chop'' [0, hstep .. last_x] 1921 { 1922 chop'' x = extract_area x y tile_width' tile_height' pad; 1923 } 1924} 1925 1926/* Reassemble image. 1927 */ 1928imagearray_assemble hoverlap voverlap il 1929 = (image_set_origin 0 0 @ foldl1 tbj @ map (foldl1 lrj)) il 1930{ 1931 lrj l r = insert (get_width l + hoverlap) 0 r l; 1932 tbj t b = insert 0 (get_height t + voverlap) b t; 1933} 1934 1935/* Generate an nxn identity matrix. 1936 */ 1937identity_matrix n 1938 = error "identity_matrix: n > 0", n < 1 1939 = map line [0 .. n - 1] 1940{ 1941 line p = take p [0, 0 ..] ++ [1] ++ take (n - p - 1) [0, 0 ..]; 1942} 1943 1944/* Incomplete gamma function Q(a, x) == 1 - P(a, x) 1945 1946 FIXME ... this is now a builtin, until we can get a nice List class 1947 (requires overloadable ':') 1948 1949gammq a x 1950 = error "bad args", x < 0 || a <= 0 1951 = 1 - gamser, x < a + 1 1952 = gammcf 1953{ 1954 gamser = (gser a x)?0; 1955 gammcf = (gcf a x)?0; 1956} 1957 */ 1958 1959/* Incomplete gamma function P(a, x) evaluated as series representation. Also 1960 * return ln(gamma(a)) ... nr in c, pp 218 1961 */ 1962gser a x 1963 = [gamser, gln] 1964{ 1965 gln = gammln a; 1966 gamser 1967 = error "bad args", x < 0 1968 = 0, x == 0 1969 = 1 // fix this 1970 { 1971 // maximum iterations 1972 maxit = 100; 1973 1974 ap = List [a + 1, a + 2 ...]; 1975 xoap = x / ap; 1976 1977 del = map product (prefixes xoap.value); 1978 1979 1980 1981 1982 1983 1984 1985/* 1986 del = map (multiply (1 / a)) (map product (prefixes xoap)) 1987 1988 del = 1989 1990 xap = iterate (multiply 1991 */ 1992 1993 /* Generate all prefixes of a list ... [1,2,3] -> [[1], [1, 2], [1, 2, 1994 * 3], [1, 2, 3, 4] ...] 1995 */ 1996 prefixes l = map (converse take l) [1..]; 1997 1998 } 1999} 2000 2001/* ln(gamma(xx)) ... nr in c, pp 214 2002 */ 2003gammln xx 2004 = gln 2005{ 2006 cof = [76.18009172947146, -86.50532032941677, 24.01409824083091, 2007 -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5]; 2008 y = take 6 (iterate (add 1) (xx + 1)); 2009 ser = 1.000000000190015 + sum (map2 divide cof y); 2010 tmp = xx + 0.5; 2011 tmp' = tmp - ((xx + 0.5) * log tmp); 2012 gln = -tmp + log (2.5066282746310005 * ser / xx); 2013} 2014 2015/* make a LUT from a scatter 2016 */ 2017buildlut x 2018 = Plot_histogram (im_buildlut x), is_Matrix x && x.width > 1 2019 = im_buildlut (Matrix x), is_matrix x && is_list_len_more 1 x?0 2020 = error (_ "bad arguments to " ++ "buildlut"); 2021 2022/* Linear regression. Return a class with the stuff we need in. 2023 * from s15.2, p 665 NR in C 2024 */ 2025linreg xes yes 2026 = obj 2027{ 2028 obj = class { 2029 // in case we ever get shown in the workspace 2030 _vislevel = 2; 2031 2032 slope = sum [t * y :: [t, y] <- zip2 tes yes] / st2; 2033 intercept = (sy - sx * slope) / ss; 2034 2035 chi2 = sum [(y - intercept - slope * x) ** 2 :: [x, y] <- zip2 xes yes]; 2036 2037 siga = (chi2 / (ss - 2)) ** 0.5 * 2038 ((1 + sx ** 2 / (ss * st2)) / ss) ** 0.5; 2039 sigb = (chi2 / (ss - 2)) ** 0.5 * (1 / st2) ** 0.5; 2040 2041 // for compat with linregw, see below 2042 q = 1.0; 2043 } 2044 2045 ss = len xes; 2046 sx = sum xes; 2047 sy = sum yes; 2048 sxoss = sx / ss; 2049 2050 tes = [x - sxoss :: x <- xes]; 2051 st2 = sum [t ** 2 :: t <- tes]; 2052} 2053 2054/* Weighted linear regression. Xes, yes and a list of deviations. 2055 */ 2056linregw xes yes devs 2057 = obj 2058{ 2059 obj = class { 2060 // in case we ever get shown in the workspace 2061 _vislevel = 2; 2062 2063 slope = sum [(t * y) / sd :: [t, y, sd] <- zip3 tes yes devs] / st2; 2064 intercept = (sy - sx * slope) / ss; 2065 2066 chi2 = sum [((y - intercept - slope * x) / sd) ** 2 :: 2067 [x, y, sd] <- zip3 xes yes devs]; 2068 2069 siga = ((1 + sx * sx / (ss * st2)) / ss) ** 0.5; 2070 sigb = (1 / st2) ** 0.5; 2071 2072 q = gammq (0.5 * (len xes - 2)) (0.5 * chi2); 2073 } 2074 2075 wt = [sd ** -0.5 :: sd <- devs]; 2076 2077 ss = sum wt; 2078 sx = sum [x * w :: [x, w] <- zip2 xes wt]; 2079 sy = sum [y * w :: [y, w] <- zip2 yes wt]; 2080 sxoss = sx / ss; 2081 2082 tes = [(x - sxoss) / sd :: [x, sd] <- zip2 xes devs]; 2083 st2 = sum [t ** 2 :: t <- tes]; 2084} 2085 2086/* Clustering: pass in a list of points, repeatedly merge the 2087 * closest two points until no two points are closer than the threshold. 2088 * Return [merged-points, corresponding-weights]. A weight is a list of the 2089 * indexes we merged to make that point, ie. len weight == how significant 2090 * this point is. 2091 * 2092 * eg. 2093 * cluster 12 [152,154,155,42,159] == 2094 * [[155,42],[[1,2,0,4],[3]]] 2095 */ 2096cluster thresh points 2097 = oo_unary_function cluster_op points, is_class points 2098 // can't use [0..len points - 1], in case len points == 0 2099 = merge [points, map (converse cons []) (take (len points) [0 ..])], 2100 is_list points 2101 = error (_ "bad arguments to " ++ "cluster") 2102{ 2103 cluster_op = Operator "cluster" 2104 (cluster thresh) Operator_type.COMPOUND false; 2105 2106 merge x 2107 = x, m < 2 || d > thresh 2108 = merge [points', weights'] 2109 { 2110 [points, weights] = x; 2111 m = len points; 2112 2113 // generate indexes of all possible pairs, avoiding comparing a thing 2114 // to itself, and assuming that dist is reflexive 2115 // first index is always less than 2nd index 2116 // the +1,+2 makes sure we have an increasing generator, otherwise we 2117 // can get [3 .. 4] (for example), which will make a decreasing 2118 // sequence 2119 pairs = [[x, y] :: x <- [0 .. m - 1]; y <- [x + 1, x + 2 .. m - 1]]; 2120 2121 // distance function 2122 // arg is eg. [3,1], meaning get distance from point 3 to point 1 2123 dist x 2124 = abs (points?i - points?j) 2125 { 2126 [i, j] = x; 2127 } 2128 2129 // smallest distance, then the two points we merge 2130 p = minpos (map dist pairs); 2131 d = dist pairs?p; 2132 [i, j] = pairs?p; 2133 2134 // new point and new weight 2135 nw = weights?i ++ weights?j; 2136 np = (points?i * len weights?i + points?j * len weights?j) / len nw; 2137 2138 // remove element i from a list 2139 remove i l = take i l ++ drop (i + 1) l; 2140 2141 // remove two old points, add the new merged one 2142 // i < j (see "pairs", above) 2143 points' = np : remove i (remove j points); 2144 weights' = nw : remove i (remove j weights); 2145 } 2146} 2147 2148/* Extract the area of an image around an arrow. 2149 * Transform the image to make the arrow horizontal, then displace by hd and 2150 * vd pxels, then cut out a bit h pixels high centered on the arrow. 2151 */ 2152extract_arrow hd vd h arrow 2153 = extract_area (re p' + hd) (im p' - h / 2 + vd) (re pv) h im' 2154{ 2155 // the line as a polar vector 2156 pv = polar (arrow.width, arrow.height); 2157 a = im pv; 2158 2159 // smallest rotation that will make the line horizontal 2160 a' 2161 = 360 - a, a > 270 2162 = 180 - a, a > 90 2163 = -a; 2164 2165 im' = rotate Interpolate_bilinear a' arrow.image; 2166 2167 // look at the start and end of the arrow, pick the leftmost 2168 p 2169 = (arrow.left, arrow.top), arrow.left <= arrow.right 2170 = (arrow.right, arrow.bottom); 2171 2172 // transform that point to im' space 2173 p' = rectangular (polar p + (0, a')) + (im'.xoffset, im'.yoffset); 2174} 2175 2176/* You'd think these would go in _convert, but they are not really colour ops, 2177 * so put them here. 2178 */ 2179rad2float image 2180 = oo_unary_function rad2float_op image, is_class image 2181 = im_rad2float image, is_image image 2182 = error (_ "bad arguments to " ++ "rad2float") 2183{ 2184 rad2float_op = Operator "rad2float" 2185 rad2float Operator_type.COMPOUND_REWRAP false; 2186} 2187 2188float2rad image 2189 = oo_unary_function float2rad_op image, is_class image 2190 = im_float2rad image, is_image image 2191 = error (_ "bad arguments to " ++ "float2rad") 2192{ 2193 float2rad_op = Operator "float2rad" 2194 float2rad Operator_type.COMPOUND_REWRAP false; 2195} 2196 2197segment x 2198 = oo_unary_function segment_op x, is_class x 2199 = image', is_image x 2200 = error (_ "bad arguments to " ++ "segment") 2201{ 2202 segment_op = Operator "segment" 2203 segment Operator_type.COMPOUND_REWRAP false; 2204 2205 [image, nsegs] = im_segment x; 2206 image' = im_copy_set_meta image "n-segments" nsegs; 2207} 2208 2209point a b 2210 = oo_binary_function point_op a b, is_class a 2211 = oo_binary'_function point_op a b, is_class b 2212 = im_read_point b x y, is_image b 2213 = [b?x?y], is_matrix b 2214 = [b?x], is_real_list b && y == 0 2215 = [b?y], is_real_list b && x == 0 2216 = error (_ "bad arguments to " ++ "point") 2217{ 2218 point_op = Operator "point" 2219 (\a\b Vector (point a b)) Operator_type.COMPOUND false; 2220 2221 (x, y) 2222 = a, is_complex a; 2223 = (a?0, a?1), is_real_list a && is_list_len 2 a 2224 = error "bad position format"; 2225} 2226 2227/* Generate an ImageMagick (or GraphicsMagick) command suitable for 2228 * im_system_image. Use convert.exe in $VIPSHOME/bin, if it exists, otherwise 2229 * assume it's on the path somewhere. 2230 */ 2231magick_command switch 2232 = join_sep " " [convert, "\"%s\"", switch, "\"%s\""] 2233{ 2234 prefs = Workspaces.Preferences; 2235 use_gm = prefs.USE_GRAPHICSMAGICK; 2236 name = if use_gm then "gm" else "convert"; 2237 exe = concat [name, expand "$EXEEXT"]; 2238 vipsexe = path_absolute [expand "$VIPSHOME", "bin", exe]; 2239 final_exe 2240 = vipsexe, search vipsexe != [] 2241 = exe; 2242 convert 2243 = join_sep " " [final_exe, "convert"], use_gm 2244 = final_exe; 2245} 2246 2247/* Run a command on an image. See magick_command, for example. 2248 */ 2249system_image command x 2250 = oo_unary_function system_image_op x, is_class x 2251 = system x, is_image x 2252 = error (_ "bad arguments to " ++ "system_image") 2253{ 2254 system_image_op = Operator "system_image" 2255 (system_image command) Operator_type.COMPOUND_REWRAP false; 2256 2257 system im 2258 = image_out 2259 { 2260 [image_out, log] = 2261 im_system_image (get_image im) "%s.tif" "%s.tif" command; 2262 } 2263} 2264