1
2Colour_new_item = class
3	Menupullright (_ "_New") (_ "make a patch of colour") {
4	Widget_colour_item = class
5		Menuaction (_ "_Colour") (_ "make a patch of colour") {
6		action = Colour_picker "Lab" [50,0,0];
7	}
8
9	LAB_colour = class
10		Menuaction (_ "CIE Lab _Picker") (_ "pick colour in CIE Lab space") {
11		action = widget "Lab" [50, 0, 0];
12
13		// ab_slice size
14		size = 512;
15
16		// range of values ... +/- 128 for ab
17		range = 256;
18
19		// map xy in slice image to ab and back
20		xy2ab x = x / (size / range) - 128;
21		ab2xy a = (a + 128) * (size / range);
22
23		widget space default_value = class
24			Colour space _result {
25			_vislevel = 3;
26
27			_L = default_value?0;
28			_a = default_value?1;
29			_b = default_value?2;
30
31			lightness = Slider 0 100 _L;
32			ab_slice = Image (lab_slice size lightness.value);
33			point = Mark ab_slice (ab2xy _a) (ab2xy _b);
34
35			_result = [lightness.value, xy2ab point.left, xy2ab point.top];
36
37			Colour_edit colour_space value = widget colour_space value;
38		}
39	}
40}
41
42#separator
43
44Colour_convert_item = class
45	Menupullright (_ "_Convert To") (_ "convert to various colour spaces") {
46	spaces = Image_type.image_colour_spaces;
47
48	conv dest x = class
49		_result {
50		_vislevel = 3;
51
52		to = Option_enum spaces (_ "Convert to") (spaces.get_name dest);
53
54		_result = map_unary (colour_transform_to to.value_thing) x;
55	}
56
57	Mono_item = class
58		Menuaction (_ "_Monochrome") (_ "convert to mono colourspace") {
59		action x = conv Image_type.B_W x;
60	}
61
62	sRGB_item = class
63		Menuaction (_ "_sRGB") (_ "convert to sRGB colourspace") {
64		action x = conv Image_type.sRGB x;
65	}
66
67	Lab_item = class
68		Menuaction (_ "_Lab") (_ "convert to Lab colourspace (float Lab)") {
69		action x = conv Image_type.LAB x;
70	}
71
72	LabQ_item = class
73		Menuaction (_ "Lab_Q") (_ "convert to LabQ colourspace (32-bit Lab)") {
74		action x = conv Image_type.LABQ x;
75	}
76
77	LabS_item = class
78		Menuaction (_ "Lab_S") (_ "convert to LabS colourspace (48-bit Lab)") {
79		action x = conv Image_type.LABS x;
80	}
81
82	LCh_item = class
83		Menuaction (_ "L_Ch") (_ "convert to LCh colourspace") {
84		action x = conv Image_type.LCH x;
85	}
86
87	XYZ_item = class
88		Menuaction (_ "_XYZ") (_ "convert to XYZ colourspace") {
89		action x = conv Image_type.XYZ x;
90	}
91
92	Yxy_item = class
93		Menuaction (_ "_Yxy") (_ "convert to Yxy colourspace") {
94		action x = conv Image_type.YXY x;
95	}
96
97	UCS_item = class
98		Menuaction (_ "_UCS") (_ "convert to UCS colourspace") {
99		action x = conv Image_type.UCS x;
100	}
101}
102
103/* mark objects as being in various colourspaces
104 */
105Colour_tag_item = class
106	Menupullright (_ "_Tag As")
107		(_ "tag object as being in various colour spaces") {
108	spaces = Image_type.image_colour_spaces;
109
110	tag dest x = class
111		_result {
112		_vislevel = 3;
113
114		to = Option_enum spaces (_ "Tag as") (spaces.get_name dest);
115
116		_result = map_unary (image_set_type to.value_thing) x;
117	}
118
119	Mono_item = class
120		Menuaction (_ "_Monochrome") (_ "tag as being in mono colourspace") {
121		action x = tag Image_type.B_W x;
122	}
123
124	sRGB_item = class
125		Menuaction (_ "_sRGB") (_ "tag as being in sRGB colourspace") {
126		action x = tag Image_type.sRGB x;
127	}
128
129	Lab_item = class
130		Menuaction (_ "_Lab")
131			(_ "tag as being in Lab colourspace (float Lab)") {
132		action x = tag Image_type.LAB x;
133	}
134
135	LabQ_item = class
136		Menuaction (_ "Lab_Q")
137			(_ "tag as being in LabQ colourspace (32-bit Lab)") {
138		action x = tag Image_type.LABQ x;
139	}
140
141	LabS_item = class
142		Menuaction (_ "Lab_S")
143			(_ "tag as being in LabS colourspace (48-bit Lab)") {
144		action x = tag Image_type.LABS x;
145	}
146
147	LCh_item = class
148		Menuaction (_ "L_Ch") (_ "tag as being in LCh colourspace") {
149		action x = tag Image_type.LCH x;
150	}
151
152	XYZ_item = class
153		Menuaction (_ "_XYZ") (_ "tag as being in XYZ colourspace") {
154		action x = tag Image_type.XYZ x;
155	}
156
157	Yxy_item = class
158		Menuaction (_ "_Yxy") (_ "tag as being in Yxy colourspace") {
159		action x = tag Image_type.YXY x;
160	}
161
162	UCS_item = class
163		Menuaction (_ "_UCS") (_ "tag as being in UCS colourspace") {
164		action x = tag Image_type.UCS x;
165	}
166}
167
168Colour_temperature_item = class
169	Menupullright (_ "Colour Te_mperature")
170		(_ "colour temperature conversions") {
171	Whitepoint_item = class
172		Menuaction (_ "_Move Whitepoint") (_ "change whitepoint") {
173		action x = class
174			_result {
175			_vislevel = 3;
176
177			old_white = Option_enum Whitepoints (_ "Old whitepoint") "D65";
178			new_white = Option_enum Whitepoints (_ "New whitepoint") "D50";
179
180			_result
181				= map_unary process x
182			{
183				process im
184					= im'''
185				{
186					im' = colour_transform_to Image_type.XYZ im;
187					im'' = im' *
188						(new_white.value_thing / old_white.value_thing);
189					im''' = colour_transform_to (get_type im) im'';
190				}
191			}
192		}
193	}
194
195	D65_to_D50_item = class
196		Menupullright (_ "D_65 to D50") (_ "complex conversion") {
197		XYZ_minimal_item = class
198			Menuaction (_ "_Minimal")
199				(_ "D65 to D50 using the minimal 3x3 matrix in XYZ") {
200			action x
201				= map_unary process x
202			{
203				process im
204					= im'''
205				{
206					im' = colour_transform_to Image_type.XYZ im;
207					im'' = recomb D652D50_direct im';
208					im''' = colour_transform_to (get_type im) im'';
209				}
210			}
211		}
212
213		Bradford_item = class
214			Menuaction (_ "_Bradford") (_ "D65 to D50 in Bradford cone space") {
215			action x
216				= map_unary process x
217			{
218				process im
219					= im'''
220				{
221					im' = colour_transform_to Image_type.XYZ im;
222					im'' = im_D652D50 im';
223					im''' = colour_transform_to (get_type im) im'';
224				}
225			}
226		}
227	}
228
229	D50_to_D65_item = class
230		Menupullright (_ "D_50 to D65") (_ "complex conversion") {
231		XYZ_minimal_item = class
232			Menuaction (_ "_Minimal")
233				(_ "D50 to D65 using the minimal 3x3 matrix in XYZ") {
234			action x
235				= map_unary process x
236			{
237				process im
238					= im'''
239				{
240					im' = colour_transform_to Image_type.XYZ im;
241					im'' = recomb D502D65_direct im';
242					im''' = colour_transform_to (get_type im) im'';
243				}
244			}
245		}
246
247		Bradford_item = class
248			Menuaction (_ "_Bradford") (_ "D60 to D65 in Bradford cone space") {
249			action x
250				= map_unary process x
251			{
252				process im
253					= im'''
254				{
255					im' = colour_transform_to Image_type.XYZ im;
256					im'' = im_D502D65 im';
257					im''' = colour_transform_to (get_type im) im'';
258				}
259			}
260		}
261	}
262
263	Lab_to_D50XYZ_item = class
264		Menuaction (_ "_Lab to D50 XYZ")
265			(_ "Lab to XYZ with a D50 whitepoint") {
266		action x = map_unary (colour_unary im_D50Lab2XYZ) x;
267	}
268
269	D50XYZ_to_Lab_item = class
270		Menuaction (_ "D50 _XYZ to Lab")
271			(_ "XYZ to Lab with a D50 whitepoint") {
272		action x = map_unary (colour_unary im_D50XYZ2Lab) x;
273	}
274}
275
276Colour_icc_item = class
277	Menupullright (_ "_ICC") (_ "transform with ICC profiles") {
278	print_profile =
279		"$VIPSHOME/share/$PACKAGE/data/cmyk.icm";
280	monitor_profile =
281		"$VIPSHOME/share/$PACKAGE/data/sRGB.icm";
282	guess_profile image
283		= monitor_profile, has_bands image && get_bands image == 3
284		= print_profile;
285	render_intents = Option_enum Render_intent.names (_ "Render intent")
286		(_ "Absolute");
287
288	Export_item = class
289		Menuaction (_ "_Export") (_ "export from PCS to device space") {
290		action x = class
291			_result {
292			_vislevel = 3;
293
294			profile = Pathname (_ "Output profile") print_profile;
295			intent = render_intents;
296			depth = Option (_ "Output depth") [_ "8 bit", _ "16 bit"] 0;
297
298			_result
299				= map_unary process x
300			{
301				process image
302					= icc_export [8, 16]?depth profile.value
303						intent.value_thing lab
304				{
305					lab = colour_transform_to Image_type.LABQ image;
306				}
307			}
308		}
309	}
310
311	Import_item = class
312		Menuaction (_ "_Import") (_ "import from device space to PCS") {
313		action x = class
314			_result {
315			_vislevel = 3;
316
317			profile = Pathname (_ "Input profile") (guess_profile x);
318			intent = render_intents;
319
320			_result
321				= map_unary process x
322			{
323				process image
324					= icc_import profile.value intent.value_thing image;
325			}
326		}
327	}
328
329	Transform_item = class
330		Menuaction (_ "_Transform") (_ "transform between two device spaces") {
331		action x = class
332			_result {
333			_vislevel = 3;
334
335			in_profile = Pathname (_ "Input profile") (guess_profile x);
336			out_profile = Pathname (_ "Output profile") print_profile;
337			intent = render_intents;
338
339			_result
340				= map_unary process x
341			{
342				process image
343					= icc_transform in_profile.value out_profile.value
344						intent.value_thing image;
345			}
346		}
347	}
348
349	AC2RC_item = class
350		Menuaction (_ "_Absolute to Relative")
351			(_ "absolute to relative colorimetry using device profile") {
352		action x = class
353			_result {
354			_vislevel = 3;
355
356			profile = Pathname (_ "Pick a profile") (guess_profile x);
357
358			_result
359				= map_unary process x
360			{
361				process image
362					= icc_ac2rc profile.value lab
363				{
364					lab = colour_transform_to Image_type.LAB image;
365				}
366			}
367		}
368	}
369}
370
371#separator
372
373Colour_dE_item = class
374	Menupullright (_ "_Difference") (_ "calculate colour difference") {
375	/* Apply a converter to an object ... convert image or colour (since
376	 * we can guess the colour space we're converting from), don't convert
377	 * matrix or vector (since we can't tell ... assume it's in the right
378	 * space already).
379	 */
380	apply_cvt cvt x
381		= cvt x,
382			is_Image x || is_Colour x || is_image x
383		= x;
384
385	diff cvt in1 in2 = abs_vec (apply_cvt cvt in1 - apply_cvt cvt in2);
386
387	/* Converter to LAB.
388	 */
389	lab_cvt = colour_transform_to Image_type.LAB;
390
391	/* Converter to UCS ... plain UCS is Ch form, so we go LAB again after
392	 * to make sure we get a rectangular coord system.
393	 */
394	ucs_cvt = colour_transform Image_type.LCH Image_type.LAB @
395		colour_transform_to Image_type.UCS;
396
397	CIEdE76_item = class
398		Menuaction (_ "CIE dE _76")
399			(_ "calculate CIE dE 1976 for two objects") {
400		action a b = map_binary (diff lab_cvt) a b;
401	}
402
403	CIEdE00_item = class
404		Menuaction (_ "CIE dE _00")
405			(_ "calculate CIE dE 2000 for two objects") {
406		action a b = map_binary
407			(colour_binary (_ "im_dE00_fromLab") im_dE00_fromLab) a b;
408	}
409
410	UCS_item = class
411		Menuaction (_ "_CMC(l:l)") (_ "calculate CMC(l:l) for two objects") {
412		action a b = map_binary (diff ucs_cvt) a b;
413	}
414}
415
416Colour_adjust_item = class
417	Menupullright (_ "_Adjust") (_ "alter colours in various ways") {
418	Recombination_item = class
419		Menuaction (_ "_Recombination")
420			(_ "recombine colour with an editable matrix") {
421		action x = class
422			_result {
423			_vislevel = 3;
424
425			matrix
426				= Matrix_rec (identity_matrix (bands x))
427			{
428				// try to guess a sensible value for the size of the
429				// matrix
430				bands x
431					= x.bands, is_Image x || is_Colour x
432					= x.width, is_Matrix x
433					= bands x.value?0, is_Group x
434					= x.bands, has_member "bands" x
435					= 3;
436			}
437
438			_result = map_unary (recomb matrix) x;
439		}
440	}
441
442	Cast_item = class
443		Menuaction (_ "_Cast") (_ "displace neutral axis in CIE Lab") {
444		action x = class
445			_result {
446			_vislevel = 3;
447
448			green_red = Slider (-20) 20 0;
449			blue_yellow = Slider (-20) 20 0;
450
451			_result
452				= map_unary adjust_cast x
453			{
454				adjust_cast in
455					= colour_transform_to (get_type in) in''
456				{
457					in' = colour_transform_to Image_type.LAB in;
458					in'' = in' +
459						Vector [0, green_red.value, blue_yellow.value];
460				}
461			}
462		}
463	}
464
465	HSB_item = class
466		Menuaction (_ "_HSB") (_ "adjust hue-saturation-brightness in LCh") {
467		action x = class
468			_result {
469			_vislevel = 3;
470
471			hue = Slider 0 360 0;
472			saturation = Slider 0.01 5 1;
473			brightness = Slider 0.01 5 1;
474
475			_result
476				= map_unary adjust_hsb x
477			{
478				adjust_hsb in
479					= colour_transform_to (get_type in) in''
480				{
481					in' = colour_transform_to Image_type.LCH in;
482					in'' = in' * Vector [bv, sv, 1] + Vector [0, 0, hv];
483					bv = brightness.value;
484					sv = saturation.value;
485					hv = hue.value;
486				}
487			}
488		}
489	}
490}
491
492Colour_similar_item = class
493	Menuaction (_ "_Similar Colour") (_ "find pixels with a similar colour") {
494	action x = class
495		_result {
496		_vislevel = 3;
497
498		target_colour = Colour_picker "Lab" [50, 0, 0];
499		dE_threshold = Slider 0 100 10;
500
501		_result
502			= map_unary match x
503		{
504			match in
505				= abs_vec (in' - target) < dE_threshold
506			{
507				target = colour_transform_to Image_type.LAB target_colour;
508				in' = colour_transform_to Image_type.LAB in;
509			}
510		}
511	}
512}
513
514#separator
515
516Colour_image_to_colour_item = class
517	Menuaction (_ "Im_age to Colour") (_ "convert image to colour") {
518	action x
519		= map_unary test x
520	{
521		test x
522			= to_colour x, is_Image x
523			= to_colour (Image pixel), is_Mark x
524			= error (_ "Colour_from_image: arg not Image or Mark: " ++
525				print x)
526		{
527			pixel = extract_area x.left x.top 1 1 x;
528		}
529	}
530}
531
532Colour_colour_to_image_item = class
533	Menuaction (_ "C_olour to _Image") (_ "convert colour to image") {
534	action x = class
535		_result {
536		_vislevel = 3;
537
538		width = Expression (_ "Width of image to make") 64;
539		height = Expression (_ "Height of image to make") 64;
540
541		_result
542			= map_unary (Image @ build) x
543		{
544			build in
545				= image_new (to_real width) (to_real height) 3
546					Image_format.FLOAT
547					Image_coding.NOCODING (get_type in) in 0 0;
548		}
549	}
550}
551
552#separator
553
554Colour_chart_to_matrix_item = class
555	Menuaction (_ "_Measure Colour Chart")
556		(_ "measure average pixel values for a colour chart image") {
557	action x = class
558		_result {
559		_vislevel = 3;
560
561		pacross = Expression (_ "Patches across chart") 6;
562		pdown = Expression (_ "Patches down chart") 4;
563
564		_result
565			= map_unary chart x
566		{
567			chart in
568				= measure 0 0 in.width in.height
569					(to_real pacross) (to_real pdown) in;
570		}
571	}
572}
573
574Colour_matrix_to_chart_item = class
575	Menuaction (_ "Make S_ynthetic Colour Chart")
576		(_ "make a colour chart image from a matrix of measurements") {
577	action x = class
578		_result {
579		_vislevel = 3;
580
581		pacross = Expression (_ "Patches across chart") 6;
582		pdown = Expression (_ "Patches down chart") 4;
583		pwidth = Expression (_ "Patch width in pixels") 50;
584		pheight = Expression (_ "Patch height in pixels") 50;
585		bwidth = Expression (_ "Border between patches") 0;
586
587		_result
588			= map_unary build_chart x
589		{
590			build_chart in
591				= Image (imagearray_assemble
592					(to_real bwidth) (to_real bwidth) patch_table)
593			{
594				// patch numbers for row starts
595				rowstart = map (multiply (to_real pacross))
596					[0 .. to_real pdown - 1];
597
598				// assemble patches ... each one a pixel value
599				patches = map (take (to_real pacross))
600					(map (converse drop in.value) rowstart);
601
602				// make an n-band constant image from eg. [1,2,3]
603				// we don't know the format .. use sRGB (well, why not?)
604				patch v = image_new (to_real pwidth) (to_real pheight) (len v)
605					Image_format.FLOAT Image_coding.NOCODING
606					Image_type.sRGB (Vector v) 0 0;
607
608				// make an image for each patch
609				patch_table = map (map patch) patches;
610			}
611		}
612	}
613}
614
615Colour_plot_ab_scatter_item = class
616	Menuaction (_ "_Plot ab Scatter") (_ "plot an ab scatter histogram") {
617	action x = class
618		_result {
619		_vislevel = 3;
620
621		bins = Expression (_ "Number of bins on each axis") 8;
622
623		_result
624			= map_unary plot_scatter x
625		{
626			plot_scatter in
627				= Image (bg * (((90 / mx) * hist) ++ blk))
628			{
629				lab = colour_transform_to Image_type.LAB in.value;
630				ab = (unsigned char) ((lab?1 ++ lab?2) + 128);
631				hist = hist_find_nD bins.expr ab;
632				mx = max hist;
633				bg = lab_slice bins.expr 1;
634				blk = 1 + im_black (to_real bins) (to_real bins) 2;
635			}
636		}
637	}
638}
639