distinguishable_colors.m 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. function colors = distinguishable_colors(n_colors,bg,func)
  2. % DISTINGUISHABLE_COLORS: pick colors that are maximally perceptually distinct
  3. %
  4. % When plotting a set of lines, you may want to distinguish them by color.
  5. % By default, Matlab chooses a small set of colors and cycles among them,
  6. % and so if you have more than a few lines there will be confusion about
  7. % which line is which. To fix this problem, one would want to be able to
  8. % pick a much larger set of distinct colors, where the number of colors
  9. % equals or exceeds the number of lines you want to plot. Because our
  10. % ability to distinguish among colors has limits, one should choose these
  11. % colors to be "maximally perceptually distinguishable."
  12. %
  13. % This function generates a set of colors which are distinguishable
  14. % by reference to the "Lab" color space, which more closely matches
  15. % human color perception than RGB. Given an initial large list of possible
  16. % colors, it iteratively chooses the entry in the list that is farthest (in
  17. % Lab space) from all previously-chosen entries. While this "greedy"
  18. % algorithm does not yield a global maximum, it is simple and efficient.
  19. % Moreover, the sequence of colors is consistent no matter how many you
  20. % request, which facilitates the users' ability to learn the color order
  21. % and avoids major changes in the appearance of plots when adding or
  22. % removing lines.
  23. %
  24. % Syntax:
  25. % colors = distinguishable_colors(n_colors)
  26. % Specify the number of colors you want as a scalar, n_colors. This will
  27. % generate an n_colors-by-3 matrix, each row representing an RGB
  28. % color triple. If you don't precisely know how many you will need in
  29. % advance, there is no harm (other than execution time) in specifying
  30. % slightly more than you think you will need.
  31. %
  32. % colors = distinguishable_colors(n_colors,bg)
  33. % This syntax allows you to specify the background color, to make sure that
  34. % your colors are also distinguishable from the background. Default value
  35. % is white. bg may be specified as an RGB triple or as one of the standard
  36. % "ColorSpec" strings. You can even specify multiple colors:
  37. % bg = {'w','k'}
  38. % or
  39. % bg = [1 1 1; 0 0 0]
  40. % will only produce colors that are distinguishable from both white and
  41. % black.
  42. %
  43. % colors = distinguishable_colors(n_colors,bg,rgb2labfunc)
  44. % By default, distinguishable_colors uses the image processing toolbox's
  45. % color conversion functions makecform and applycform. Alternatively, you
  46. % can supply your own color conversion function.
  47. %
  48. % Example:
  49. % c = distinguishable_colors(25);
  50. % figure
  51. % image(reshape(c,[1 size(c)]))
  52. %
  53. % Example using the file exchange's 'colorspace':
  54. % func = @(x) colorspace('RGB->Lab',x);
  55. % c = distinguishable_colors(25,'w',func);
  56. % Copyright 2010-2011 by Timothy E. Holy
  57. % Parse the inputs
  58. if (nargin < 2)
  59. bg = [1 1 1]; % default white background
  60. else
  61. if iscell(bg)
  62. % User specified a list of colors as a cell aray
  63. bgc = bg;
  64. for i = 1:length(bgc)
  65. bgc{i} = parsecolor(bgc{i});
  66. end
  67. bg = cat(1,bgc{:});
  68. else
  69. % User specified a numeric array of colors (n-by-3)
  70. bg = parsecolor(bg);
  71. end
  72. end
  73. % Generate a sizable number of RGB triples. This represents our space of
  74. % possible choices. By starting in RGB space, we ensure that all of the
  75. % colors can be generated by the monitor.
  76. n_grid = 30; % number of grid divisions along each axis in RGB space
  77. x = linspace(0,1,n_grid);
  78. [R,G,B] = ndgrid(x,x,x);
  79. rgb = [R(:) G(:) B(:)];
  80. if (n_colors > size(rgb,1)/3)
  81. error('You can''t readily distinguish that many colors');
  82. end
  83. % Convert to Lab color space, which more closely represents human
  84. % perception
  85. if (nargin > 2)
  86. lab = func(rgb);
  87. bglab = func(bg);
  88. else
  89. C = makecform('srgb2lab');
  90. lab = applycform(rgb,C);
  91. bglab = applycform(bg,C);
  92. end
  93. % If the user specified multiple background colors, compute distances
  94. % from the candidate colors to the background colors
  95. mindist2 = inf(size(rgb,1),1);
  96. for i = 1:size(bglab,1)-1
  97. dX = bsxfun(@minus,lab,bglab(i,:)); % displacement all colors from bg
  98. dist2 = sum(dX.^2,2); % square distance
  99. mindist2 = min(dist2,mindist2); % dist2 to closest previously-chosen color
  100. end
  101. % Iteratively pick the color that maximizes the distance to the nearest
  102. % already-picked color
  103. colors = zeros(n_colors,3);
  104. lastlab = bglab(end,:); % initialize by making the "previous" color equal to background
  105. for i = 1:n_colors
  106. dX = bsxfun(@minus,lab,lastlab); % displacement of last from all colors on list
  107. dist2 = sum(dX.^2,2); % square distance
  108. mindist2 = min(dist2,mindist2); % dist2 to closest previously-chosen color
  109. [~,index] = max(mindist2); % find the entry farthest from all previously-chosen colors
  110. colors(i,:) = rgb(index,:); % save for output
  111. lastlab = lab(index,:); % prepare for next iteration
  112. end
  113. end
  114. function c = parsecolor(s)
  115. if ischar(s)
  116. c = colorstr2rgb(s);
  117. elseif isnumeric(s) && size(s,2) == 3
  118. c = s;
  119. else
  120. error('MATLAB:InvalidColorSpec','Color specification cannot be parsed.');
  121. end
  122. end
  123. function c = colorstr2rgb(c)
  124. % Convert a color string to an RGB value.
  125. % This is cribbed from Matlab's whitebg function.
  126. % Why don't they make this a stand-alone function?
  127. rgbspec = [1 0 0;0 1 0;0 0 1;1 1 1;0 1 1;1 0 1;1 1 0;0 0 0];
  128. cspec = 'rgbwcmyk';
  129. k = find(cspec==c(1));
  130. if isempty(k)
  131. error('MATLAB:InvalidColorString','Unknown color string.');
  132. end
  133. if k~=3 || length(c)==1,
  134. c = rgbspec(k,:);
  135. elseif length(c)>2,
  136. if strcmpi(c(1:3),'bla')
  137. c = [0 0 0];
  138. elseif strcmpi(c(1:3),'blu')
  139. c = [0 0 1];
  140. else
  141. error('MATLAB:UnknownColorString', 'Unknown color string.');
  142. end
  143. end
  144. end