Настраиваемая цветовая карта MATLAB, доступная по имени цветовой карты в виде строки (как и цветовые карты по умолчанию) - PullRequest
0 голосов
/ 28 ноября 2018

При использовании цветовых карт MATLAB по умолчанию многие функции могут получать доступ к картам либо по массиву rgb, либо по строке с тем же именем, что и у этого массива rgb.Например, в приведенном ниже коде любая из линий с функцией colormap будет отображаться как задумано.

x = linspace(0, 255, 255);
y = linspace(0, 255, 255);
[X Y] = meshgrid(x, y);

figure, imagesc(x, y, X);
% colormap(jet); % either this line or the one below it works.
colormap('jet'); % either this line or the one above it works.
colorbar();

Однако то же самое не верно для пользовательских цветовых карт - доступ только к массиву rgbработает, и доступ к имени массива rgb в виде строки не дает:

hex = ['#ff0000'; '#00ff00';];
vec = [100; 0];
raw = sscanf(hex','#%2x%2x%2x',[3, size(hex,1)] ).' / 255;
my_map = interp1(vec,raw,linspace(100, 0, 256),'pchip');

x = linspace(0, 255, 255);
y = linspace(0, 255, 255);
[X Y] = meshgrid(x, y);

figure, imagesc(x, y, X);
colormap(my_map); % this line works, the one below it does not.
colormap('my_map'); % this line does not work.
colorbar();

Что я могу сделать, чтобы я мог построить свою собственную карту цветов, когда к ней обращаются в виде строки, поэтомучто строка, которая не работает выше, будет работать?

(Кстати, одна из причин, по которой я хочу это сделать, заключается в том, что я использую функции, для которых у меня нет прямого доступа к коду, которые принимают строкув качестве аргумента для цветовой карты. Поэтому, чтобы использовать эти функции с пользовательскими цветными картами, мне нужно, чтобы мои пользовательские цветовые карты были доступны в виде строк, как и стандартные цветовые карты MATLAB.)

Спасибоза любую помощь.

Ответы [ 2 ]

0 голосов
/ 13 декабря 2018

Причина, по которой вышеприведенное не работает, заключается в том, что colormap использует feval, поэтому в исходном сообщении строка, которая не работает (colormap('my_map');), выдает следующую ошибку:

Error using feval
Undefined function or variable 'my_map'.

Способ обойти это - создать файл .m в папке в пути MATLAB, который использует функцию feval, и сохранить файл mat с цветовой картой rgb, к которой он обращается.Т.е. в папке в пути MATLAB создайте два файла:

% file 1: my_map.m
function my_map_1 = my_map()
    load('my_map_mat');
    my_map = my_map_mat; % file 2: my_map_mat.mat is a mat file with rgb values for the custom colormap.
end

Ниже приведена функция, которую я написал, которая делает это.Я не очень-то очищал ее от своей версии функции перед публикацией, но ключевые части находятся внизу, начиная со строки eval([cmap_name '_mat = cmap_1;']);

% cmap_name : name of custom colormap
% cmap_data : 1- or 3-column matrix, or name of text file with 1 or 3 columns, containing colormap data.
% by default, interpolates custom colormap to have 256 rgb values.

function cmap(cmap_name, cmap_data, varargin)

    pnames = {'output_folder', 'method', 'map_length'};
    dflts  = {'path\to\folder\in\MATLAB\path\where\you\want\to\store\custom\colormaps\', 'nearest', 256};

    [output_folder, method, map_length] = internal.stats.parseArgs(pnames, dflts, varargin{:});

    if ~ strcmp(cmap_name, lower(cmap_name))
        disp(['cmap_name changed from ' cmap_name 'to ' lower(cmap_name) '.']);
    end
    cmap_name = lower(cmap_name); % Windows has issues if you don't do this

    if ~ (any(ismember(output_folder(1 : end - 1), regexp(path, pathsep, 'Split'))) || strcmp(output_folder(1 : end - 1), pwd))
        error('output folder must be in present working directory or MATLAB path. To add it to MATLAB path, use addpath function.');
    end

    if strcmp(class(cmap_data), 'char')
        fid = fopen(cmap_data);
        cmap_1 = textscan(fid, '%f');
        cmap_1 = cmap_1{1};
        fclose(fid);
    elseif strcmp(class(cmap_data), 'double')
        cmap_1 = cmap_data;
    else
        error('cmap_data must be double arr, or name of text file containing doubles.')
    end

    if min(flat(cmap_1)) < 0 || max(flat(cmap_1)) > 1
        error('cmap_data must not contain values below 0 or above 1.');
    end

    cmap_size = size(cmap_1);

    if ~ ismember(cmap_size(2), [1 3])
        error('cmap_data must be array or name of text file with either 1 column or 3 columns.');
    end

    if isequal(cmap_size(2), 1)
        cmap_1 = [cmap_1 cmap_1 cmap_1];
    end

    if ~ (cmap_size(1) == map_length)
        cmap_1 = interp1(1:length(cmap_1), cmap_1, linspace(1, length(cmap_1), map_length), method);
    end

    eval([cmap_name '_mat = cmap_1;']);
    save([output_folder cmap_name '_mat.mat'], [cmap_name '_mat']);

    fid = fopen([output_folder cmap_name '.m'], 'w');
    function_str = {
        ['function my_map = ' cmap_name '()']...
        ['    load(''' cmap_name '_mat'');']...
        ['    my_map = ' cmap_name '_mat;']...
        ['end']...
        };
    for i = 1:length(function_str)
        fprintf(fid, '%s\n', strcat(function_str{i}));
    end
    fclose(fid);

end

Так что теперь это можно использовать, например:, следующим образом:

hex = ['#ff0000'; '#00ff00';];
vec = [100; 0];
raw = sscanf(hex','#%2x%2x%2x',[3, size(hex,1)] ).' / 255;
my_map = interp1(vec,raw,linspace(100, 0, 256),'pchip');
cmap('my_map', my_map);

x = linspace(0, 255, 255);
y = linspace(0, 255, 255);
[X Y] = meshgrid(x, y);

figure, imagesc(x, y, X);
colormap(my_map); % this line works, as does the line below it.
colormap('my_map'); % this line works, as does the line above it.
colorbar();
0 голосов
/ 28 ноября 2018

Некоторым быстрым, грязным и опасным способом достижения этой цели будет:

%Code that will kinda work
try colormap(S); catch,colormap(eval(S));

Обратите внимание, что eval - это проблема безопасности, потому что он будет выполнять код независимо от того, что находится внутри.Возьмите эти значения для S, например: S = '! rm -Rf *'; или S = 'delete(''*'')', вызов eval(S) удалит содержимое текущего каталога.

Возможно, лучший способ:

function colormap_custom(S)
    % These are default maps in R2014a, but it may change with the version => maintenance hell in the making
    defaultMaps = {'autumn', 'bone', 'colorcube', 'cool', 'copper', 'flag', 'gray', 'hot', 'hsv', 'jet', 'lines', 'pink', 'prism', 'spring', 'summer', 'white', 'winter'};

    assert(ischar(S),'S is expected to be a char array');
    if any(strcmp(S,defaultMaps))
    colormap(S);
    elseif ~isempty(who(S)) %Check that S is actually the name of a variable
        tmpS = eval(S);
        assert(isnumeric(tmpS) && size(tmpS,2)==3,'S : n-by-3 numeric array expected');
        colormap(tmpS)
    else error('unknow colormap value for S')
    end
end

Тогда:

mymap=rand(64,3); %random RGB colormap
colormap_custom('jet');     % gives colormap jet
colormap_custom('mymap');   % gives custom colormap
colormap_custom('yourmap'); % error
...