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