Tasks_capture_item = class Menupullright "_Capture" "useful stuff for capturing and preprocessing images" { Csv_import_item = class Menuaction "_CSV Import" "read a file of comma-separated values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; start_line = Expression "Start at line" 1; rows = Expression "Lines to read (-1 for whole file)" (-1); whitespace = String "Whitespace characters" " \""; separator = String "Separator characters" ",;\t"; _result = Image blank, path.value == "empty" = Image (im_csv2vips filename) { filename = search (expand path.value) ++ ":" ++ "skip:" ++ print (start_line.expr - 1) ++ "," ++ "whi:" ++ escape whitespace.value ++ "," ++ "sep:" ++ escape separator.value ++ "," ++ "line:" ++ print rows.expr; // prefix any ',' with a '\' in the separators line escape x = foldr prefix [] x { prefix x l = '\\' : x : l, x == ',' = x : l; } blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } Raw_import_item = class Menuaction "_Raw Import" "read a file of binary values" { action = class _result { _vislevel = 3; path = Pathname "File to load" "empty"; across = Expression "Pixels across" 100; down = Expression "Pixels down" 100; bytes = Expression "Bytes per pixel" 3; skip = Expression "Skip over initial bytes" 0; _result = Image blank, path.value == "empty" = Image (im_binfile path.value across.expr down.expr bytes.expr skip.expr) { blank = image_new 1 1 1 Image_format.DOUBLE Image_coding.NOCODING Image_type.B_W 0 0 0; } } } // interpret Analyze header for layout and calibration Analyze7_header_item = class Menuaction "_Interpret Analyze 7 Header" "examine the Analyze header and set layout and value" { action x = x''' { // read bits of header dim n = get_header ("dsr-image_dimension.dim[" ++ print n ++ "]"); dim0 = dim 0 x; dim1 = dim 1 x; dim2 = dim 2 x; dim3 = dim 3 x; dim4 = dim 4 x; dim5 = dim 5 x; dim6 = dim 6 x; dim7 = dim 7 x; glmax = get_header "dsr-image_dimension.glmax" x; cal_max = get_header "dsr-image_dimension.cal_max" x; // oops, now a nop x' = x; // lay out higher dimensions width-ways x'' = grid dim2 dim3 1 x', dim0 == 3 = grid dim2 dim3 dim4 x', dim0 == 4 = grid (dim2 * dim4) dim5 1 (grid dim2 dim3 dim4) x', dim0 == 5 = grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4) x', dim0 == 6 = grid (dim2 * dim4 * dim6) dim7 1 (grid (dim2 * dim4) dim5 dim6 (grid dim2 dim3 dim4)) x', dim0 == 7 = error (_ "unsupported dimension " ++ dim0); // multiply by scale factor to get kBeq x''' = x'' * (cal_max / glmax); } } Video_item = class Menuaction "Capture _Video Frame" "capture a frame of still video" { // shortcut to prefs prefs = Workspaces.Preferences; action = class _result { _vislevel = 3; device = prefs.VIDEO_DEVICE; channel = Option "Input channel" [ "TV", "Composite 1", "Composite 2", "Composite 3" ] prefs.VIDEO_CHANNEL; b = Scale "Brightness" 0 32767 prefs.VIDEO_BRIGHTNESS; col = Scale "Colour" 0 32767 prefs.VIDEO_COLOUR; con = Scale "Contrast" 0 32767 prefs.VIDEO_CONTRAST; hue = Scale "Hue" 0 32767 prefs.VIDEO_HUE; frames = Scale "Frames to average" 0 100 prefs.VIDEO_FRAMES; mono = Toggle "Monochrome grab" prefs.VIDEO_MONO; crop = Toggle "Crop image" prefs.VIDEO_CROP; // grab, but hide it ... if we let the crop edit _raw_grab = Image (im_video_v4l1 device channel.value b.value col.value con.value hue.value frames.value); edit_crop = Region _raw_grab left top width height { left = prefs.VIDEO_CROP_LEFT; top = prefs.VIDEO_CROP_TOP; width = min_pair prefs.VIDEO_CROP_WIDTH (_raw_grab.width + left); height = min_pair prefs.VIDEO_CROP_HEIGHT (_raw_grab.height + top); } aspect_ratio = Expression "Stretch vertically by" prefs.VIDEO_ASPECT; _result = frame' { frame = edit_crop, crop = _raw_grab; frame' = colour_transform_to Image_type.B_W frame, mono = frame; } } } Smooth_image_item = class Menuaction "_Smooth" "remove small features from image" { action in = class _result { _vislevel = 3; feature = Scale "Minimum feature size" 1 50 20; _result = map_unary (smooth feature.value) in; } } Light_correct_item = class Menuaction "_Flatfield" "use white image w to flatfield image i" { action w i = map_binary wc w i { wc w i = clip2fmt i.format (w' * i) { fac = mean w / max w; w' = fac * (max w / w); } } } Image_rank_item = Filter_rank_item.Image_rank_item; Tilt_item = Filter_tilt_item; sep1 = Menuseparator; White_balance_item = class Menuaction "_White Balance" "use average of small image to set white of large image" { action a b = class _result { _vislevel = 3; white_hint = "Set image white to:"; white = Colour_picker "Lab" [100, 0, 0]; _result = map_binary wb a b { wb a b = colour_transform_to (get_type image) image_xyz' { area x = x.width * x.height; larger x y = area x > area y; [image, patch] = sortc larger [a, b]; to_xyz = colour_transform_to Image_type.XYZ; // white balance in XYZ patch_xyz = to_colour (to_xyz patch); white_xyz = to_xyz white; facs = (mean patch_xyz / mean white_xyz) * (white_xyz / patch_xyz); image_xyz = to_xyz image; image_xyz' = image_xyz * facs; } } } } Gamma_item = Image_levels_item.Gamma_item; Tone_item = Image_levels_item.Tone_item; sep2 = Menuseparator; Crop_item = Image_crop_item; Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Rubber_item = Image_transform_item.Image_rubber_item; sep3 = Menuseparator; ICC_item = Colour_icc_item; Temp_item = Colour_temperature_item; Find_calib_item = class Menuaction "Find _Colour Calibration" "find an RGB -> XYZ transform from an image of a colour chart" { action image = class _result { _check_args = [ [image, "image", check_Image] ]; _vislevel = 3; measure = Scale (_ "Measure area (%)") 1 100 50; sample = measure_draw 6 4 (to_real measure) image; // get macbeth data file to use macbeth = Pathname "Pick a Macbeth data file" "$VIPSHOME/share/$PACKAGE/data/macbeth_lab_d65.mat"; mode = Option "Input LUT" [ "Linearize from chart greyscale", "Fit intercept from chart greyscale", "Linear input, set brightness from chart", "Linear input" ] 0; // get max of input image _max_value = Image_format.maxval image.format; // measure chart image _camera = measure_sample 6 4 (to_real measure) image; // load true values _true_Lab = Matrix_file macbeth.value; _true_XYZ = colour_transform Image_type.LAB Image_type.XYZ _true_Lab; // get Ys of greyscale _true_grey_Y = map (extract 1) (drop 18 _true_XYZ.value); // camera greyscale (all bands) _camera_grey = drop 18 _camera.value; // normalise both to 0-1 and combine _camera_grey' = map (map (multiply (1 / _max_value))) _camera_grey; _true_grey_Y' = map (multiply (1 / 100)) _true_grey_Y; _comb = Matrix (map2 cons _true_grey_Y' _camera_grey'), mode == 0 = Matrix [0: intercepts, replicate (_camera.width + 1) 1], mode == 1 = Matrix [[0, 0], [1, 1]] { intercepts = [(linreg _true_grey_Y' cam).intercept :: cam <- transpose _camera_grey']; } // make a linearising lut ... zero on left _linear_lut = im_invertlut _comb (_max_value + 1); // and display it // plot from 0 explicitly so we see the effect of mode1 (intercept // from greyscale) linearising_lut = Plot [$ymin => 0] _linear_lut; // map an image though the lineariser linear x = hist_map linearising_lut.value x, mode == 0 || mode == 1 = x; // map the chart measurements though the lineariser _camera' = (to_matrix @ linear @ to_image) _camera; // solve for RGB -> XYZ // normalise: the 2nd row is what makes Y, so divide by that to // get Y in 0-1. _pinv = (transpose _camera' * _camera') ** -1; _full_M = transpose (_pinv * (transpose _camera' * _true_XYZ)); M = _full_M / scale; scale = sum _full_M.value?1; // now turn the camera to LAB and calculate dE76 _camera'' = (to_matrix @ colour_transform Image_type.XYZ Image_type.LAB @ recomb M @ multiply scale @ to_image) _camera'; _dEs = map abs_vec (_camera'' - _true_Lab).value; avg_dE76 = mean _dEs; _max_dE = foldr max_pair 0 _dEs; _worst = index (equal _max_dE) _dEs; worst_patch = name _worst ++ " (patch " ++ print (_worst + 1) ++ ", " ++ print _max_dE ++ " dE)" { name i = macbeth_names?i, i >= 0 && i < len macbeth_names = "Unknown"; } // normalise brightness ... in linear mode, we optionally don't // set the brightness from the Macbeth chart norm x = x * scale, mode != 3 = x; // convert RGB camera to Lab _result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ norm @ recomb M @ cast_float @ linear) image.value; } } Apply_calib_item = class Menuaction "_Apply Colour Calibration" "apply an RGB -> LAB transform to an image" { action a b = class (map_binary process a b) { process a b = result, is_instanceof calib_name calib && is_Image image = error (_ "bad arguments to " ++ "Calibrate_image") { // the name of the calib object we need calib_name = "Tasks_capture_item.Find_calib_item.action"; // get the Calibrate_chart arg first [image, calib] = sortc (const (is_instanceof calib_name)) [a, b]; result = (Image @ colour_transform Image_type.XYZ Image_type.LAB @ calib.norm @ recomb calib.M @ cast_float @ calib.linear) image.value; } } } sep4 = Menuseparator; Graph_hist_item = Hist_find_item; Graph_bands_item = class Menuaction "Plot _Bands" "show image bands as a graph" { action x = class _result { _vislevel = 3; style = Option_enum "Style" Plot_style.names "Line"; auto = Toggle "Auto Range" true; ymin = Expression "Y range minimum" 0; ymax = Expression "Y range maximum" 1; _result = Plot options (to_image (bands (image x))).value { options = [$style => style.value] ++ if auto then [] else [$ymin => ymin.expr, $ymax => ymax.expr]; // try to make something image-like from it image x = extract_area x.left x.top 1 1 x.image, is_Mark x = get_image x, has_image x = get_image (to_image x); // get as [[1],[2],[3]] bands x = transpose [map mean (bandsplit x)]; } } } } Tasks_mosaic_item = class Menupullright "_Mosaic" "build image mosaics" { /* Check and group a point list by image. */ mosaic_sort_test l = error "mosaic: not all points", !is_listof is_Mark l = error "mosaic: points not on two images", !is_list_len 2 images = error "mosaic: images do not match in format and coding", !all_equal (map get_format l) || !all_equal (map get_coding l) = error "mosaic: not same number of points on each image", !foldr1 equal (map len l') = l' { // test for all elements of a list equal all_equal l = all (map (equal (hd l)) (tl l)); // all the different images images = mkset pointer_equal (map get_image l); // find all points defined on image test_image image p = (get_image p) === image; find l image = filter (test_image image) l; // group point list by image l' = map (find l) images; } /* Sort a point group to get right before left, and within each group to * get above before below. */ mosaic_sort_lr l = l'' { // sort to get upper point first above a b = a.top < b.top; l' = map (sortc above) l; // sort to get right group before left group right a b = a?0.left > b?0.left; l'' = sortc right l'; } /* Sort a point group to get top before bottom, and within each group to * get left before right. */ mosaic_sort_tb l = l'' { // sort to get upper point first left a b = a.left < b.left; l' = map (sortc left) l; // sort to get right group before left group below a b = a?0.top > b?0.top; l'' = sortc below l'; } /* Put 'em together! Group by image, sort vertically (or horizontally) with * one of the above, transpose to get pairs matched up, and flatten again. */ mosaic_sort fn = concat @ transpose @ fn @ mosaic_sort_test; Mosaic_1point_item = class Menupullright "_One Point" "join two images with a single tie point" { check_ab_args a b = [ [a, "a", check_Mark], [b, "b", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; lr_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_lrmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_lr [a, b]; } } tb_mos _refine a b = class Image _result { _check_args = check_ab_args a b; bw = blend_width_widget; refine = _refine; _result = im_tbmosaic a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge a'.image.value b'.image.value (b'.left - a'.left) (b'.top - a'.top) bw.value { [a', b'] = mosaic_sort mosaic_sort_tb [a, b]; } } Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a single tie point" { action a b = lr_mos refine_widget a b; } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a single tie point" { action a b = tb_mos refine_widget a b; } sep1 = Menuseparator; Left_right_manual_item = class Menuaction "Manual L_eft to Right" "join left-right, no auto-adjust of tie points" { action a b = lr_mos false a b; } Top_bottom_manual_item = class Menuaction "Manual T_op to Bottom" "join top-bottom, no auto-adjust of tie points" { action a b = tb_mos false a b; } } Mosaic_2point_item = class Menupullright "_Two Point" "join two images with two tie points" { check_abcd_args a b c d = [ [a, "a", check_Mark], [b, "b", check_Mark], [c, "c", check_Mark], [d, "d", check_Mark] ]; // shortcut to prefs prefs = Workspaces.Preferences; search_area = prefs.MOSAIC_WINDOW_SIZE; object_size = prefs.MOSAIC_OBJECT_SIZE; blend_width_widget = Scale "Maximum blend width" 0 100 prefs.MOSAIC_MAX_BLEND_WIDTH; refine_widget = Toggle "Refine selected tie-points" prefs.MOSAIC_REFINE; Left_right_item = class Menuaction "_Left to Right" "join two images left-right with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_lrmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_lrmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_lr [a, b, c, d]; } } } Top_bottom_item = class Menuaction "_Top to Bottom" "join two images top-bottom with a pair of tie points" { action a b c d = class Image _result { _check_args = check_abcd_args a b c d; bw = blend_width_widget; refine = refine_widget; _result = im_tbmosaic1 a'.image.value b'.image.value 0 a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top (object_size / 2) (search_area / 2) 0 bw.value, refine = im_tbmerge1 a'.image.value b'.image.value a'.left a'.top b'.left b'.top c'.left c'.top d'.left d'.top bw.value { [a', b', c', d'] = mosaic_sort mosaic_sort_tb [a, b, c, d]; } } } } sep1 = Menuseparator; Balance_item = class Menuaction "Mosaic _Balance" "disassemble mosaic, scale brightness to match, reassemble" { action x = map_unary balance x { balance x = oo_unary_function balance_op x, is_class x = im_global_balancef x Workspaces.Preferences.MOSAIC_BALANCE_GAMMA, is_image x = error (_ "bad arguments to " ++ "balance") { balance_op = Operator "balance" balance Operator_type.COMPOUND_REWRAP false; } } } //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// Manual_balance_item = class Menupullright "Manual B_alance" "balance tonality of user defined areas" { prefs = Workspaces.Preferences; //////////////////////////////////////////////////////////////////////////////////// Balance_find_item = class Menuaction "_Find Values" "calculates values required to scale and offset balance user defined areas in a given image" /* Outputs a matrix of scale and offset values. Eg. Values required to balance the secondary * structure in an X-ray image. Takes an X-ray image an 8-bit control mask and a list of * 8-bit reference masks, where the masks are white on a black background. */ { action im_in m_control m_group = class Matrix values{ _vislevel = 1; _control_im = if m_control then im_in else 0; _control_meanmax = so_meanmax _control_im; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; process m_current mat_in = mat_out {so_values = so_calculate _control_meanmax im_in m_current; mat_out = join [so_values] mat_in;} values = (foldr process [] _m_list); } } //////////////////////////////////////////////////////////////////////////////////// Balance_check_item = class Menuaction "_Check Values" "allows calculated set of scale and offset values to be checked and adjusted if required" /* Outputs adjusted matrix of scale and offset values and scale and offset image maps. * Eg. Check values required to balance the secondary structure in an X-ray image. * Takes an X-ray image an 8-bit control mask and a list of 8-bit reference masks, * where the masks are white on a black background. */ { action im_in m_matrix m_group = class Image value { _vislevel = 3; blur = Scale "Blur" 1 10 1; _blur = (blur.value/2 + 0.5), blur.value > 1 = 1; _group_check = is_Group m_group; _m_list = m_group.value, _group_check = m_group; adjust = Matrix_rec mat_a { no_masks = len _m_list; mat_a = replicate no_masks [0, 0]; } // Apply the user defined adjustments to the inputted matrix of scale and offset values _adjusted = map2 fn_adjust m_matrix.value adjust.value; fn_adjust a b = [(a?0 + b?0), (a?1 + (a?1 * b?1))]; _scaled_ims = map (fn_so_apply im_in) _adjusted; fn_so_apply im so = map_unary adj im {adj im = im * (so?0) + (so?1);} _im_pairs = zip2 _m_list _scaled_ims; // Prepare black images as starting point. //////////// _blank = image_new (_m_list?0).width (_m_list?0).height 1 6 Image_coding.NOCODING 1 0 0 0; _pair_start = [(_blank + 1), _blank]; Build = Toggle "Build Scale and Offset Correction Images" false; Output = class { _vislevel = 1; scale_im = _build?0; offset_im = _build?1; so_values = Matrix _adjusted; _build = [Image so_images?0, Image so_images?1], Build = ["Scale image not built.", "Offset image not built."] { m_list' = transpose [_m_list]; m_all = map2 join m_list' _adjusted; so_images = foldr process_2 _pair_start m_all; } } value = (foldr process_1 im_in_b _im_pairs).value {im_in_b = map_unary cast_float im_in;} process_1 m_current im_start = im_out { bl_mask = convsep (matrix_blur _blur) (get_image m_current?0); blended_im = im_blend bl_mask (m_current?1).value im_start.value; im_out = Image (clip2fmt im_start.format blended_im); } // Process for building scale and offset image. process_2 current p_start = p_out { im_s = if ((current?0) > 128) then current?1 else _blank; im_o = if ((current?0) > 128) then current?2 else _blank; im_s' = convsep (matrix_blur _blur) (im_s != 0); im_o' = convsep (matrix_blur _blur) (im_o != 0); im_s'' = im_blend im_s'.value im_s.value p_start?0; im_o'' = im_blend im_o'.value im_o.value p_start?1; p_out = [im_s'', im_o'']; } } } //////////////////////////////////////////////////////////////////////////////////// Balance_apply_item = class Menuaction "_Apply Values" "apply scale and offset corrections, defined as image maps, to a given image" /* Outputs the balanced image. Eg. Balance the secondary structure in an X-ray image. Takes an * X-ray image an 32-bit float scale image and a 32-bit offset image. */ { action im_in scale_im offset_im = class Image value { _vislevel = 1; xfactor = im_in.width/scale_im.width; yfactor = im_in.height/scale_im.height; _scale_im = resize Interpolate_bilinear xfactor yfactor scale_im; _offset_im = resize Interpolate_bilinear xfactor yfactor offset_im; value = get_image ( clip2fmt im_in.format ( ( im_in * _scale_im ) + _offset_im ) ); } } } Tilt_item = Filter_tilt_item; sep2 = Menuseparator; Rebuild_item = class Menuaction "_Rebuild" "disassemble mosaic, substitute image files and reassemble" { action x = class _result { _vislevel = 3; old = String "In each filename, replace" "foo"; new = String "With" "bar"; _result = map_unary remosaic x { remosaic image = Image (im_remosaic image.value old.value new.value); } } } sep3 = Menuseparator; Clone_area_item = class Menuaction "_Clone Area" "replace dark or light section of im1 with pixels from im2" { action im1 im2 = class _result { _check_args = [ [im1, "im1", check_Image], [im2, "im2", check_Image] ]; _vislevel = 3; /* Region on first image placed in the top left hand corner, * positioned and size relative to the height and width of im1. */ r1 = Region_relative im1 0.05 0.05 0.05 0.05; /* Mark on second image placed in the top left hand corner, * positioned relative to the height and width of im2. Used to * define _r2, the region from which the section of image is cloned * from. */ p2 = Mark_relative im2 0.05 0.05; _r2 = Region im2 p2.left p2.top r1.width r1.height; mask = [r1 <= Options.sc, r1 >= Options.sc]?(Options.replace); Options = class { _vislevel = 3; pause = Toggle "Pause process" true; /* Option toggle used to define whether the user is * replacing a dark or a light area. */ replace = Option "Replace" [ "A Dark Area", "A Light Area" ] 1; // Used to select the area to be replaced. sc = Scale "Scale cutoff" 0.01 mx (mx / 2) {mx = Image_format.maxval im1.format;} //Allows replacement with scale&offset balanced gaussian noise. balance = Toggle "Balance cloned data to match surroundings." true; //Allows replacement with scale&offset balanced //gaussian noise. process = Toggle "Replace area with Gaussian noise." false; } _result = im1, Options.pause = Image (im_insert im1.value patch r1.left r1.top) { r2 = Region im2 p2.left p2.top r1.width r1.height; ref_meanmax = so_meanmax (if mask then 0 else r1); mask8 = Matrix_mor [[255, 255, 255], [255, 255, 255], [255, 255, 255]]; mask_a = map_unary (dilate mask8) mask; mask_b = convsep (matrix_blur 2) mask_a; patch = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.balance = so_balance ref_meanmax r1 r2 mask_b Options.process, Options.process = im_blend (get_image mask_b) (get_image r2) (get_image r1); } } } } Tasks_frame_item = Frame_item; Tasks_print_item = class Menupullright "_Print" "useful stuff for image output" { Rotate_item = Image_transform_item.Rotate_item; Flip_item = Image_transform_item.Flip_item; Resize_item = Image_transform_item.Resize_item; Tone_item = Image_levels_item.Tone_item; Sharpen_item = class Menuaction "_Sharpen" "unsharp filter tuned for typical inkjet printers" { action x = class _result { _vislevel = 3; target_dpi = Option "Sharpen for print at" [ "400 dpi", "300 dpi", "150 dpi", "75 dpi" ] 1; _result = map_unary process x { process image = sharpen params?0 params?1 params?2 params?3 params?4 params?5 (colour_transform_to Image_type.LABQ image) { // sharpen params for various dpi // just change the size of the area we search param_table = [ [7, 2.5, 40, 20, 0.5, 1.5], [5, 2.5, 40, 20, 0.5, 1.5], [3, 2.5, 40, 20, 0.5, 1.5], [11, 2.5, 40, 20, 0.5, 1.5] ]; params = param_table?target_dpi; } } } } sep1 = Menuseparator; Temp_item = Colour_temperature_item; ICC_item = Colour_icc_item; }