Более быстрая версия функции dec2bin для преобразования многих элементов?

Я читаю файл растрового изображения и преобразовываю каждое из значений RGB в диапазоне от 0 до 255 в двоичный файл.

Таким образом, растровое изображение 240 на 320 будет иметь 230400 значений RGB для преобразования. Первоначальная функция dec2bin была слишком медленной, поэтому я написал свою собственную, поскольку знаю, что мое значение всегда будет в диапазоне от 0 до 255.

Но прохождение значений 230400 все равно займет ок. 6 секунд на моей машине, и одноцветное растровое изображение займет около 2,3 секунды.

Есть ли способ ускорить процесс до 1 секунды или даже лучше 0,5 секунды, поскольку каждая мс считается для моего приложения?

Вот мой код:

function s = dec2bin_2(input)

if input == 255
    s = [1;1;1;1;1;1;1;1];
    return;
end

s = [0;0;0;0;0;0;0;0];

if input == 0
    return;
end

if input >= 128
    input = input - 128;
    s(1) = 1;
    if input == 0
        return;
    end
end

if input >= 64
    input = input - 64;
    s(2) = 1;
    if input == 0
        return;
    end
end

if input >= 32
    input = input - 32;
    s(3) = 1;
    if input == 0
        return;
    end
end

if input >= 16
    input = input - 16;
    s(4) = 1;
    if input == 0
        return;
    end
end

if input >= 8
    input = input - 8;
    s(5) = 1;
    if input == 0
        return;
    end
end

if input >= 4
    input = input - 4;
    s(6) = 1;
    if input == 0
        return;
    end
end

if input >= 2
    input = input - 2;
    s(7) = 1;
    if input == 0
        return;
    else
        s(8) = 1;
    end
end
end

Я думал, что если я не смогу сделать это в MATLAB, то, возможно, я сделаю преобразование в C++. Это желательно?

Благодарю.

4 ответа

Решение

Еще более быстрый способ - использовать справочные таблицы. Поскольку вы знаете, что все значения являются интенсивностями от 0 до 255, вы создаете двоичный эквивалент каждого из них, чтобы ускорить процесс.

% build table (computed once) [using gnovice option#1]
lookupTable = cell2mat(arrayfun(@(i)bitget([0:255]',9-i),1:8,'UniformOutput',0));

% random' image
I = randi(256, [240 320])-1;

% decimal to binary conversion
binI = lookupTable(I(:)+1,:);

На моей машине это заняло в среднем 0.0036329 секунд (только преобразование). Обратите внимание, что в таблице поиска почти нет места:

>> whos lookupTable
  Name               Size            Bytes  Class    Attributes
  lookupTable      256x8              2048  uint8 

Вариант № 1: зацикливание на каждом пикселе и использование BITGET

Вы можете зациклить каждый пиксель (или значение RGB) на своем изображении и использовать BITGET, чтобы получить вектор нулей и единиц. Вот пример того, как использовать BITGET:

>> bitget(uint8(127),8:-1:1)  % Get bits 8 through 1 for a uint8 value

ans =

    0    1    1    1    1    1    1    1

Вариант № 2: Векторизованное решение с BITGET

Можно создать векторизованное решение, в котором вы перебираете каждый бит вместо каждого пикселя, выполняя операцию BITGET для всей матрицы изображения каждый раз в цикле. Ниже приведена одна из таких реализаций:

function B = get_bits(A,N)
  % Gets the N lowest bits from each element of A
  B = zeros([size(A) 0]);
  nDims = ndims(A)+1;
  for iBit = N:-1:1
    B = cat(nDims,B,bitget(A,iBit));
  end
end

Если матрица A это 2-D (n-by-m) или 3-D (n-by-m-by-p), матрица B будет на одно измерение больше. Дополнительное измерение будет иметь размер N с наибольшим битом в индексе 1. Вы можете либо индексировать в этом измерении, чтобы получить значение бита, либо изменить форму B в более легко визуализируемой форме. Вот пример использования:

>> A = uint8([126 128; 127 129]);  % A 2-by-2 matrix of uint8 values
>> B = get_bits(A,8);              % B is a 2-by-2-by-8 matrix
>> B(:,:,1)                        % Get bit 8 for each value in A

ans =

     0     1
     0     1

>> reshape(B,4,8)                  % Reshape B into a 4-by-8 matrix

ans =

     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     1
     1     0     0     0     0     0     0     0
     1     0     0     0     0     0     0     1

Разве вы не можете использовать bitand, чтобы получить биты напрямую?

s(0) = 256 bitand input
s(1) = 128 bitand input
s(2) = 64 bitand input

так далее...

Проблема такого рода (выполнение операции для каждого элемента в большом массиве, поскольку встроенный код Matlab слишком медленный) иногда требует решения в Java, поскольку Matlab работает на JRE, а преобразование / передача аргументов массива обычно довольно быстрая операция.

Решение gnovice звучит так, как будто оно работает для вас, но если вы столкнулись с ситуацией, которую вы не можете решить в чистом Matlab, и вы владеете Java, подумайте о написании собственного файла JAR. Это довольно легко. (ну, намного проще, чем пытаться связать C++ с Matlab!)

Другие вопросы по тегам