1Hist_new_item = class
2	Menuaction "_New Histogram" "make a new histogram" {
3	action = class
4		_result {
5		_vislevel = 3;
6
7		d = Option "Depth" ["8 bit", "16 bit"] 0;
8		b = Scale "Black point"  0 100 0;
9		w = Scale "White point"  0 100 100;
10
11		sp = Scale "Shadow point" 0.1 0.3 0.2;
12		mp = Scale "Mid-tone point" 0.4 0.6 0.5;
13		hp = Scale "Highlight point" 0.7 0.9 0.8;
14
15		sa = Scale "Shadow adjust" (-15) 15 0;
16		ma = Scale "Mid-tone adjust" (-30) 30 0;
17		ha = Scale "Highlight adjust" (-15) 15 0;
18
19		_result
20			= tone_build fmt b w sp mp hp sa ma ha
21		{
22			fmt = [Image_format.UCHAR, Image_format.USHORT]?d;
23		}
24	}
25}
26
27Hist_new_from_matrix = Matrix_buildlut_item;
28
29Hist_from_image_item = class
30	Menuaction "Ta_g Image As Histogram" "set image Type field to Histogram" {
31	action x = hist_tag x;
32}
33
34#separator
35
36Hist_find_item = class
37	Menupullright "_Find Histogram" "find a histogram" {
38	Oned_item = class
39		Menuaction "_One Dimension"
40			"for a n-band image, make an n-band 1D histogram" {
41		action x = map_unary hist_find x;
42	}
43
44	Nd_item = class
45		Menuaction "_Many Dimensions"
46			"for a n-band image, make an n-dimensional histogram" {
47		action x = class
48			_result {
49			_vislevel = 3;
50
51			// default to something small-ish
52			bins = Expression "Number of bins in each dimension" 8;
53
54			_result
55				= map_unary process x
56			{
57				process in
58					= hist_find_nD bins in;
59			}
60		}
61	}
62}
63
64Hist_map_item = class
65	Menuaction "_Map Histogram" "map an image through a histogram" {
66	action x y
67		= map_binary map x y
68	{
69		map a b
70			= hist_map hist im
71		{
72			args = sortc (const is_hist) [a, b];
73			im = args?0;
74			hist = args?1;
75		}
76	}
77}
78
79Hist_eq_item = Filter_enhance_item.Hist_equal_item;
80
81#separator
82
83Hist_cum_item = class
84	Menuaction "_Cumulativise Histogram"
85		"form cumulative histogram" {
86	action x = map_unary hist_cum x;
87}
88
89Hist_diff_item = class
90	Menuaction "_Differentiate Histogram"
91		"find point-to-point differences (inverse of Cumulativise)" {
92	action x = map_unary hist_diff x;
93}
94
95Hist_norm_item = class
96	Menuaction "N_ormalise Histogram" "normalise a histogram" {
97	action x = map_unary hist_norm x;
98}
99
100Hist_match_item = class
101	Menuaction "Ma_tch Histogram"
102		"find LUT which will match first histogram to second" {
103	action in ref = map_binary hist_match in ref;
104}
105
106Hist_zerox_item = class
107	Menuaction "_Zero Crossings" "find zero crossings" {
108	action x = class
109		_result {
110		_vislevel = 3;
111
112		edge = Option "Direction" [
113			"Positive-going",
114			"Negative-going"
115		] 0;
116
117		_result
118			= map_unary (zerox (if edge == 0 then -1 else 1)) x;
119	}
120}
121
122#separator
123
124Hist_profile_item = class
125	Menuaction "Find _Profile"
126		"search from image edges for non-zero pixels" {
127	action x = class
128		_result {
129		_vislevel = 3;
130
131		edge = Option "Search from" [
132			"Top edge down",
133			"Left edge to right",
134			"Bottom edge up",
135			"Right edge to left"
136		] 2;
137
138		_result
139			= map_unary profile x
140		{
141			profile image
142				= (Plot_histogram @ hist_tag) [
143					profilemb 0 image.value,
144					profilemb 1 image.value,
145					profilemb 0 (fliptb image.value),
146					profilemb 1 (fliplr image.value)
147				]?edge;
148
149			// im_profile only does 1 band images :-(
150			profilemb d = bandjoin @ map (converse im_profile d) @ bandsplit;
151		}
152	}
153}
154
155Hist_project_item = class
156	Menuaction "Find Pro_jections"
157		"find horizontal and vertical projections" {
158	action x = class {
159		_vislevel = 2;
160
161		_result = map_unary project x;
162
163		// extract the result ... could be a group
164		extr n
165			= Plot_histogram _result?n, is_list _result
166			= Group (map (Plot_histogram @ converse subscript n) _result.value);
167
168		horizontal = extr 0;
169		vertical = extr 1;
170		centre = (gravity horizontal, gravity vertical);
171	}
172}
173
174#separator
175
176Hist_graph_item = class
177	Menuaction "P_lot Slice" "plot a slice along a guide or arrow" {
178	action x = class
179		_value {
180		_vislevel = 3;
181
182		width = Scale "Width" 1 40 1;
183		displace = Scale "Horizontal displace" (-50) 50 0;
184		vdisplace = Scale "Vertical displace" (-50) 50 0;
185
186		_value
187			= map_unary graph x
188		{
189			graph arrow
190				= hist_tag area'
191			{
192				// the line as a polar vector
193				pv = polar (arrow.width, arrow.height);
194				a = im pv;
195
196				// smallest rotation that will make the line horizontal
197				a'
198					= 360 - a, a > 270
199					= 180 - a, a > 90
200					= -a;
201
202				im' = rotate a' arrow.image;
203
204				// look at the start and end of the arrow, pick the leftmost
205				p
206					= (arrow.left, arrow.top), arrow.left <= arrow.right
207					= (arrow.right, arrow.bottom);
208
209				// transform that point to im' space
210				p' = rectangular (polar p + (0, a')) +
211					(im'.xoffset, im'.yoffset);
212
213				// extract that area
214				area = extract_area
215					(re p' + displace.value)
216					(im p' - width.value / 2 + vdisplace.value)
217					(re pv) width.value
218					im';
219
220				// squish vertically to get an average
221				area' = resize 1 (1 / width.value) Interpolate.BILINEAR area;
222			}
223		}
224	}
225}
226
227Extract_arrow_item = class
228	Menuaction "Extract _Arrow" "extract the area around an arrow" {
229	action x = class
230		_value {
231		_vislevel = 3;
232
233		width = Scale "Width" 1 40 1;
234		displace = Scale "Horizontal displace" (-50) 50 0;
235		vdisplace = Scale "Vertical displace" (-50) 50 0;
236
237		_value
238			= map_unary graph x
239		{
240			graph arrow
241				= area
242			{
243				// the line as a polar vector
244				pv = polar (arrow.width, arrow.height);
245				a = im pv;
246
247				// smallest rotation that will make the line horizontal
248				a'
249					= 360 - a, a > 270
250					= 180 - a, a > 90
251					= -a;
252
253				im' = rotate a' arrow.image;
254
255				// look at the start and end of the arrow, pick the leftmost
256				p
257					= (arrow.left, arrow.top), arrow.left <= arrow.right
258					= (arrow.right, arrow.bottom);
259
260				// transform that point to im' space
261				p' = rectangular (polar p + (0, a')) +
262					(im'.xoffset, im'.yoffset);
263
264				// extract that area
265				area = extract_area
266					(re p' + displace.value)
267					(im p' - width.value / 2 + vdisplace.value)
268					(re pv) width.value
269					im';
270			}
271		}
272	}
273}
274
275Hist_plot_item = class
276	Menuaction "Plot _Object"
277		"plot an object as a bar, point or line graph" {
278	action x = class
279		_result {
280		_vislevel = 3;
281
282		format = Option_enum Plot_format.names "Format" "YYYY";
283		style = Option_enum Plot_style.names "Style" "Line";
284
285		auto = Toggle "Auto Range" true;
286		xmin = Expression "X range minimum" 0;
287		xmax = Expression "X range maximum" 1;
288		ymin = Expression "Y range minimum" 0;
289		ymax = Expression "Y range maximum" 1;
290
291		_result
292			= Plot options (image x)
293		{
294			options
295				= [["style", style.value], ["format", format.value]] ++ range;
296			range
297				= [], auto
298				= [["xmin", xmin.expr], ["xmax", xmax.expr],
299					["ymin", ymin.expr], ["ymax", ymax.expr]];
300
301			image x
302				= image (extract_arrow x), is_Arrow x
303				= get_image x, has_image x
304				= x2b im, b == 1
305				= im
306			{
307				im = get_image (to_image x);
308				w = get_width im;
309				h = get_height im;
310				b = get_bands im;
311
312				// matrix to image makes a 1-band mxn image
313				// we need to put columns into bands
314				x2b im
315					= bandjoin (map extract_col [0 .. w - 1])
316				{
317						extract_col x = extract_area x 0 1 h im;
318				}
319			}
320
321			extract_arrow arrow
322				= extract_area (re p') (im p') (re pv) 1 im'
323			{
324				// the line as a polar vector
325				pv = polar (arrow.width, arrow.height);
326				a = im pv;
327
328				// smallest rotation that will make the line horizontal
329				a'
330					= 360 - a, a > 270
331					= 180 - a, a > 90
332					= -a;
333
334				im' = rotate a' arrow.image;
335
336				// look at the start and end of the arrow, pick the leftmost
337				p
338					= (arrow.left, arrow.top), arrow.left <= arrow.right
339					= (arrow.right, arrow.bottom);
340
341				// transform that point to im' space
342				p' = rectangular (polar p + (0, a')) +
343					(im'.xoffset, im'.yoffset);
344			}
345		}
346	}
347}
348