/* ******Functions included in start/_NG_utilities.def:****** * * so_balance ref_meanmax im1 im2 mask blur gauss * * nonzero_mean im = no_out * * so_meanmax im = result * * so_calculate ref_meanmax im mask = result * * simple_frame frame im_w im_h ov cs ms bf option = result * * corner_frame frame im_w im_h ov cs ms bf = result * * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * * complex_frame frame im_w im_h ov cs es ms bf option= result * * complex_edge ra rb t bl d = rc * * frame_lr_min r_l r_r target bw = result * * frame_tb_min r_t r_b target bw = result * * frame_position_image im ref os colour= result * * merge_array bw arr = result * * merge_to_scale im target blend dir = result * * select_ellipse line width = mask * * select_tetragon p1 p2 p3 p4 = mask * * select_polygon pt_list = mask * * perspective_transform to from = trans'' * * sort_pts_clockwise l = l'' * */ /* Called from: * _NG_Extra.def Clone_area_item */ so_balance ref_meanmax im1 im2 mask gauss = result { //ref_meanmax = so_meanmax im1; so_values = so_calculate ref_meanmax im2 mask; im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 = im2'' {im2'' = im2 * (so_values?0) + (so_values?1);} // Option to convert replacement image to scaled gaussian noise im2_cor = im2_cor_a, gauss == false = clip2fmt im2_cor_a.format gauss_im {gauss_im = im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 (deviation im2_cor_a);} result = im_blend (get_image mask) (get_image im2_cor) (get_image im1); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the mean of the non zero pixels. * * Called from: * _NG_utilities so_meanmax */ nonzero_mean im = no_out { zero_im = (im == 0); zero_mean = mean zero_im; no_mean = mean im; no_out = no_mean/(1 - (zero_mean/255)); }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the max and nonzero mean of an image * * Called from: * _NG_utilities so_balance * _NG_utilities so_calculate * _NG_Extra.def Clone_area_item * _NG_Extra.def Balance_item.Balance_find_item */ so_meanmax im = result { mean_of_im = nonzero_mean im; adjusted_im = im - mean_of_im; max_of_im = max adjusted_im; result = [mean_of_im, max_of_im]; }; //////////////////////////////////////////////////////////////////////////////// /* Calculates the scale and offset required to match a reference mean and max * * Called from: * _NG_utilities so_balance * _NG_Extra.def Balance_item.Balance_find_item */ so_calculate ref_meanmax im mask = result { im' = if_then_else mask im 0; im_values = so_meanmax im'; mean_of_ref = ref_meanmax?0; mean_of_im = im_values?0; max_of_ref = ref_meanmax?1; max_of_im = im_values?1; scale = (max_of_ref)/(max_of_im); offset = mean_of_ref - (mean_of_im * scale); result = [ scale, offset ]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a simple frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Simple_frame_item */ simple_frame frame im_w im_h ov cs ms bf option = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); ms'' = (1 - cs); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' ms'' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame ms'' ms' cs ms; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item */ corner_frame frame im_w im_h ov cs ms bf = result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); //Regions r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl; r_bl = fliptb r_tl; r_br = fliplr r_bl; r_mt = Region_relative frame ms' 0 ms cs; r_mb = fliptb r_mt; r_ml = Region_relative frame 0 ms' cs ms;; r_mr = fliplr r_ml; result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; }; //////////////////////////////////////////////////////////////////////////////// /* Completes the frame building process for simple_frame and corner_frame. * * _NG_utilities simple_frame * _NG_utilities corner_frame */ build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result { //Find pixel thickness of frames section s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); blend = bf * r_tl.width; cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) = w_target; ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) = h_target; //Use regions to produce sections top = merge_to_scale r_mt cw_target blend 0; bottom = merge_to_scale r_mb cw_target blend 0; left = merge_to_scale r_ml ch_target blend 1; right = merge_to_scale r_mr ch_target blend 1; middle = Image (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); //Build sections into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Extends or shortens the central sections of a frame, preserving any central details on each * edge, to fit round a given image. * * Called from: * _NG_Extra.def Frame_item.Complex_frame_item */ complex_frame frame im_w im_h ov cs es ms bf option= result { cs' = (1 - cs); ms' = (0.5 - (ms/2)); es' = (0.25 - (es/2)); r_tl = Region_relative frame 0 0 cs cs; r_tr = fliplr r_tl, option == true = Region_relative frame cs' 0 cs cs; r_bl = Region_relative frame 0 cs' cs cs; r_br = fliplr r_bl, option == true = Region_relative frame cs' cs' cs cs; r_mt = Region_relative frame ms' 0 ms cs; r_mb = Region_relative frame ms' cs' ms cs; r_ml = Region_relative frame 0 ms' cs ms; r_mr = fliplr r_ml, option == true = Region_relative frame cs' ms' cs ms; r_et = Region_relative frame es' 0 es cs; r_eb = Region_relative frame es' cs' es cs; r_el = Region_relative frame 0 es' cs es; r_er = fliplr r_el, option == true = Region_relative frame cs' es' cs es; //Find pixel thickness of frames section s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); w_target = im_w + (2 * (s_width - ov)); h_target = im_h + (2 * (s_height - ov)); min_size = foldr1 min_pair [r_tl.width, r_tl.height, r_mt.width, r_mt.height, r_et.width, r_et.height]; blend = bf * min_size; cw_target = w_target - (2 * r_tl.width) + (2 * blend); ch_target = h_target - (2 * r_tl.height) + (2 * blend); top = complex_edge r_mt r_et cw_target blend 0; bottom = complex_edge r_mb r_eb cw_target blend 0; left = complex_edge r_ml r_el ch_target blend 1; right = complex_edge r_mr r_er ch_target blend 1; middle = Image (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); //Build regions into full frame. row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_tl, top, r_tr]]; row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[left, middle, right]]; row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) = merge_array blend [[r_bl, bottom, r_br]]; result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) = merge_array blend [[row_1], [row_2], [row_3]]; }; //////////////////////////////////////////////////////////////////////////////// /* Function called by complex frame, used to produce section * * Called from: * _NG_utilities.def complex_frame */ complex_edge ra rb t bl d = rc { e1 = ceil (ra.width - t)/2, d == 0 = 0; e2 = 0, d == 0 = ceil (ra.height - t)/2; e3 = t, d == 0 = ra.width; e4 = ra.height, d == 0 = t; check = ra.width, d == 0; = ra.height; rai = get_image ra; t2 = (t - ra.width + (2 * bl))/2, d == 0 = (t - ra.height + (2 * bl))/2; rc = ra , t <= 0 = Image (im_extract_area rai e1 e2 e3 e4), t <= check = merge_array bl [[rb',ra,rb']], d == 0 = merge_array bl [[rb'],[ra],[rb']] {rb' = merge_to_scale rb t2 bl d;} } ////////////////////////////////////////////////////////////////////////////// /* Blends two images left/right to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_lr_min r_l r_r target bw = result { //Calculating the new widh required for each image. no = (target/2 + bw); n_w = no, (r_l.width > no) = r_l.width; //Removing excess from what will be the middle of the final image. n_l = im_extract_area r_l.value 0 0 n_w r_l.height; n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; //Merge the two image together with a bw*2 pixel overlap. result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); }; ////////////////////////////////////////////////////////////////////////////// /* Blends two images top/bottom to produce an image a specific width. * * _NG_utilities build_frame * _NG_utilities complex_frame */ frame_tb_min r_t r_b target bw = result { //Calculating the new height required for each image. no = (target/2 + bw); n_h = no, (r_t.height > no) = r_t.height; //Removing excess from what will be the middle of the final image. n_t = im_extract_area r_t.value 0 0 r_t.width n_h; n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; //Merge the two image together with a 50 pixel overlap. result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); }; ////////////////////////////////////////////////////////////////////////////// /* Resixe canvas of an image to accomodate a frame and possible mount * * Called from: * _NG_Extra.def Frame_item.Frame_corner_item * _NG_Extra.def Frame_item.Simple_frame_item * _NG_Extra.def Frame_item.Complex_frame_item */ frame_position_image im ref os colour= result { background = image_new ref.width ref.height im.bands im.format im.coding im.type colour 0 0; result = insert_noexpand xp yp im background { xp = (ref.width - im.width)/2; yp = (ref.height - im.height - os)/2; } }; ////////////////////////////////////////////////////////////////////////////// /* Merges an array of images together according to blend width bw * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_frame * _NG_Utilites.def complex_edge */ merge_array bw arr = result { merge_lr bw im1 im2 = im3 { bw' = im_header_int "Xsize" (get_image im1); bw'' = -(bw' - bw); im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; } merge_tb bw im1 im2 = im3 { bw' = im_header_int "Ysize" (get_image im1); bw'' = -(bw' - bw); im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; } im_out = (image_set_origin 0 0 @ foldl1 (merge_tb bw) @ map (foldl1 (merge_lr bw))) arr; result = Image im_out; } ////////////////////////////////////////////////////////////////////////////// /* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target * * Called from: * _NG_Utilites.def build_frame * _NG_Utilites.def complex_edge */ merge_to_scale im target blend dir = result { blend' = floor blend; //allow fir lr or tb process var_a = im.width, dir == 0 = im.height; var_w = im.width, dir == 1 = target, target > blend' = blend'; var_h = im.height, dir == 0 = target, target > blend' = blend'; //total numner of copies of im requires, taking overlap into account. no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); process im no = result { pr_a = im_header_int "Xsize" (get_image im), dir == 0 = im_header_int "Ysize" (get_image im); pr_b = -(pr_a - blend' + 1); im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; no' = no - 1; result = im', no' < 1 = process im' no'; } im_tmp = im.value, var_a > target = process im no_loops; result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); } ////////////////////////////////////////////////////////////////////////////// /* Selects an elispe based on a line and a width * * Called from: * _NG_Extra.def Select_item.Elipse */ select_ellipse line width = mask { im = line.image; //Make a 2 band image whose value equals its coordinates. im_coor = Image (make_xy im.width im.height); //Adjust the values to center tham on (line.left, line.top) im_cent = im_coor - Vector [line.left,line.top]; w = line.width; h = line.height; angle = 270, w == 0 && h < 0 = 90, w == 0 && h >= 0 = 360 + atan (h/w), w > 0 && h < 0 = atan (h/w), w > 0 && h >= 0 = 180 + atan (h/w); a = ( (h ** 2) + (w ** 2) )**0.5; b = a * width; x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Tetragon * _NG_Extra.def Perspective_item */ select_tetragon p1 p2 p3 p4 = mask { //Put points in clockwise order starting at the top left. pt_list = sort_pts_clockwise [p1, p2, p3, p4]; pair_list = [ [ pt_list?0, pt_list?1 ], [ pt_list?1, pt_list?2 ], [ pt_list?2, pt_list?3 ], [ pt_list?3, pt_list?0 ] ]; //Make xy image the same size as p1.image; im_xy = Image (make_xy p1.image.width p1.image.height); white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); mask = foldl process white pair_list; /* Treat each pair of point as a vector going from p1 to p2, * then select all to right of line. This is done for each pair, * the results are all combined to select the area defined by * the four points. */ process im_in pair = im_out { x = (pair?0).left; y = (pair?0).top; x'= (pair?1).left; y'= (pair?1).top; w = x' - x; h = y' - y; m = 0, x == x' = (y-y')/(x-x'); c = 0, x == x' = ((y*x') - (y'*x))/(x' - x); mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 = im_xy?0 <= x, w == 0 && h > 0 = im_xy?0 >= x; im_out = im_in & mask; } } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Select_item.Polygon */ select_polygon pt_list = mask { group_check = is_instanceof "Group" pt_list; pt_l = pt_list.value, group_check = pt_list; im = (pt_l?0).image; im_xy = Image (make_xy im.width im.height); black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); x = im_xy?0; y = im_xy?1; pt_l' = grp_trip pt_l; mask = foldl process black pt_l'; /*Takes a group adds the first two the end and then creates a lists of *lists [[a, b, c], [b, c, d] .... [x, a, b]] */ grp_trip l = l'' { px = take 2 l; l' = join l px; start = [(take 3 l')]; rest = drop 3 l'; process a b = c { x = (last a)?1; x'= (last a)?2; x'' = [[x, x', b]]; c = join a x''; } l'' = foldl process start rest; }; process im_in triplet = im_out { p1 = triplet?0; p2 = triplet?1; p3 = triplet?2; //check for change in x direction between p1-p2 and p2 -p3 dir_1 = sign (p2.left - p1.left); dir_2 = sign (p3.left - p2.left); dir = dir_1 + dir_2; //define min x limit. min_x = p1.left, p1.left < p2.left = p2.left + 1, dir != 0 = p2.left; //define max x limit. max_x = p1.left, p1.left > p2.left = p2.left - 1, dir != 0 = p2.left; //equation of line defined by p1 and p2 m = line_m p1 p2; c = line_c p1 p2; //Every thing below the line im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); im_out = im_in ^ im_test; } line_c p1 p2 = c {m = line_m p1 p2; c = p1.top - (m * p1.left);}; line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left = 0; } ////////////////////////////////////////////////////////////////////////////// /* Selects a tetragon based on four points. * * Called from: * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ perspective_transform to from = trans'' { /* * Tramsformation matrix is calculated on the bases of the following functions: * x' = c0x + c1y + c2xy + c3 * y' = c4x + c5y + c6xy + c7 * * The functions used in vips im_transform works based on the functions: * x = x' + b0 + b2x' + b4y' + b6x'y' * y = y' + b1 + b3x' + b5y' + b7x'y' * * and is applied in the form of the matrix: * * [[b0, b1], * [b2, b3], * [b4, b5], * [b6, b7]] * * Therefore our required calculated matrix will be * * [[ c3 , c7], * [(c0 - 1) , c4], * [ c1 , (c5 - 1)], * [ c2 , c6]] * * to = [x1, y1, x2, y2, x3, y3, x4, y4] * from = [x1', y1', x2', y2', x3', y3', x4', y4'] * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] * */ to' = Matrix [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; from' = Matrix (transpose [from]); to'' = to' ** (-1); trans = to'' * from'; trans' = trans.value; trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], [((trans'?0)?0 - 1), (trans'?4)?0 ], [(trans'?1)?0, ((trans'?5)?0 - 1)], [(trans'?2)?0, (trans'?6)?0 ]]; } ////////////////////////////////////////////////////////////////////////////// /* Sort a list of points into clockwise order. * * Called from: * _NG_utilities.def select_tetragon * _NG_Extra.def Perspective_match_item * _NG_Extra.def Perspective_item */ sort_pts_clockwise l = l'' { // sort functions: f_top a b = a.top < b.top; f_left a b = a.left < b.left; f_right a b = a.left > b.left; l' = sortc f_top l; l'_a = take 2 l'; l'_b = drop 2 l'; l''_a = sortc f_left l'_a; l''_b = sortc f_right l'_b; l'' = join l''_a l''_b; }