Векторизация в среде MATLAB для петли, чтобы манипулировать пикселей в режиме RGB



Я все еще учусь о MATLAB и я пытаюсь понять векторизации. Я думаю, что корень моей проблемы в том, что я не понимаю, как ссылаться на различные матрицы и тому подобное. Я надеюсь, что ответ на этот вопрос поможет мне понять.



FI = imread(ForegroundImageName);
BI = imread(BackgroundImageName);
refRows =size(FI,1);
refCols =size(FI,2);
refChan =size(FI,3);
CommonRGB = mode(mode(FI));
BI = imresize(BI, [refRows refCols]);
swappedPixels = 0;

for row=1:refRows
for col=1:refCols
if(FI(row,col,:)==CommonRGB)
FI(row,col,:)=BI(row,col,:);
swappedPixels = swappedPixels + 1;
end
end
end


Фон этого вопроса состоит в том, что я заменяю пиксель переднего плана на пиксель фона, если пиксель переднего плана соответствует наиболее распространенному цвету. CommonRGB-это матрица 1x1x3, поскольку mode(mode(FI)) выплевывает ее. Изображения являются 3D цветные изображения RGB. Я выбрал это как простой пример моей проблемы. Этот цикл for выполняет то, что я хочу, чтобы он сделал, и, кажется, работает. Просто требуется много времени, чтобы пройти через цикл for. Я выполняю mode(mode(FI)) - это доли времени, которое требуется для выполнения вышеупомянутого цикла for, и я пытался реализовать mode(mode(FI)) сам, и это становится довольно сложным по сравнению с вышеописанной заменой пикселей. У меня были похожие проблемы при создании гистограммы. Я надеюсь, что вы можете помочь мне узнать больше о matlab и расширить мой Программирование знаний на этом языке. Я знаю, что будет проще, если я скажу, что хочу выполнить эти операции на всей матрице, поэтому, если мы векторизуем ее, я бы предположил, что нам не нужныrefRows, refCols, и refChan переменные в этом случае.

Передний планФонФон Заменен



Одна из моих неудачных попыток



if(FI(:,:,:)==CommonRGB)
FI(:,:,:)=BI(:,:,:);
swappedPixels = swappedPixels + 1;
end


Из ответов до сих пор они показали методы векторизации логики. Вывод, который я получаю, заключается в том, что любой другой метод, который не включает в себя длинные циклы, будет невероятно быстрее, чем использование больших петель, как я сделал. Даже если это означает создание дополнительных массивов и выполнение дополнительных процессов, таких как создание массива масок и многократное повторение изображения. Корень проблемы из того, что я могу сказать и догадаться, заключается в том, что JIT Just In Time компилятор matlab должен повторно обрабатывать команду на каждой итерации цикла for. Этот разбор и работа с циклом for является реальным корнем проблемы скорости. Если бы matlab мог "видеть" цикл for и планировать заранее, то он побежит быстрее. Таким образом, я ничего не могу сделать с исходным кодом, где я бы просто заменил row и col чем-то другим и удалил два цикла for. Мне пришлось бы разработать другие методы,которые не используют большие циклы for. Только тогда он будет работать с разумной скоростью. Это говорит мне о том, что matlab-это настоящий скриптовый язык на переднем конце, так как запуск сценариев с циклами for также страдает от снижения производительности. Поэтому я не уверен, как обозначить этот вопрос как ответили, не ответили или проголосовали за другие ответы. Поскольку если я изменю логику внутри циклов for, то мне придется изменить способ реализации ускорения.

Итак, если кто-то не может показать мне, как просто заменить переменные row и col внутри цикла for На что-то другое, например 1:refRows, использовать parfor, использовать что-то простое, что только изменяет код внутри цикла for и удаляет циклы for, или может просто написать ответ, подтверждающий абзац выше, то я не знаю, что делать в данный момент. этот момент.



Как сказал @Divakar: "вы правы, что здесь нет четкой техники векторизации, чтобы просто подключить параметры цикла и получить векторное решение из него. Если бы у вас был один цикл вместо этой ситуации с двумя вложенными циклами, вы могли бы получить более прямое векторное решение plug-n-play с использованием логического индексирования. Но да, векторизация не может быть обобщена в большинстве случаев и должна рассматриваться как основа для каждого случая. Удачи с векторизацией исследования! - Дивакар "



Тем не менее, я хотел бы помочь другим людям, показав им, что я сделал, чтобы применить то, что я узнал из этого вопроса. Мне показалось, что у меня уже есть алгоритм, который я думаю, хотя я все еще не уверен, работает ли мой выбор фазы 3 общего оттенка с использованием порога освещенности. Я не вижу, чтобы он выбирал другой оттенок, когда световой порог повышается. Я просто вижу, что фаза 3 последовательно выбирает другой оттенок, чем фаза 2. Я этого не понимал. до сегодняшнего дня. Не важно для самого вопроса, но ниже важно для вопроса, потому что я упоминаю метод цикла for и затем реализую векторизированный подход. Тем более в фазе 2-3, чем в фазе 1, где мне не нужно было сильно менять его. ... ну, это не позволило мне вставить так много информации, поэтому я попробую другой метод.

Мой PDF-файл лабораторного отчета с кодом и фотографиями
Я знаю, что режим (mode (FI)) неверен, но мне все еще интересно, как фаза 2 получает другой оттенок, чем фаза 3. Я думаю, что фаза 2 правильна, и я сравнил пиксель за пикселем в одно время, чтобы проверить, что фаза 2 такая же, как фаза 3, но режим(FI(:)) должен быть правильным. Дай мне знать, если увидишь какие-то проблемы. Я понял большую часть векторизации, за исключением того, как именно работает маска в качестве индекса. Я знаю, что они называют это логическим индексом, но я думаю, что просто наличие 1 означает, что он будет использовать его, а ноль означает, что он не будет смотреть на это значение.

651   4  

4 ответов:

Есть два очень важных инструмента для векторизации:- bsxfun и еще logical indexing. Теперь bsxfun работает с логическими операторами, поэтому только с этими двумя функциями вы можете получить большую часть вашего векторизованного кода.

bsxfun и logical indexing в обработке изображений

Как и когда использовать logical indexing?

в случаях, когда при работе с изображениями RGB и особенно при необходимости индексирования в двух измерениях (как это имеет место здесь), он может получить сложно, так как вы не можете непосредственно использовать логическое индексирование там.

Одним из приемов, которые можно было бы использовать в таких случаях, было бы использование reshape, при условии, что индексация, требуемая для остальных измерений, равномерно структурирована, что опять-таки удовлетворяется в этом проблемном случае.

Как и когда использовать bsxfun?

bsxfun находит оптимальное решение в различных задачах. Для данной задачи мы можем использовать его для многих проверок сравнения равенства, являющихся выполненные в векторной моде.

Это делается путем преобразования CommonRGB в размер такой, что существует синглетное измерение, соответствующее объединенному измерению row и col в преобразованном FI. bsxfun затем заботится о столь необходимом "expansion" для переформованного CommonRGB и выполняет проверку равенства с помощью встроенного в него дескриптора функции @eq.

Давайте перейдем к векторизованному коду, который использует эти две функции и методы, рассмотренные ранее. -
%// Reshape the inputs and save as new variables
FIr = reshape(FI,[],3);
BIr = reshape(BI,[],3);
CommonRGBr = reshape(CommonRGB,[],3);

%// Create 1D equality mask corresponding the equality satisfied across 
%// two dimensions - `row` and `col`
mask = all(bsxfun(@eq,FIr,CommonRGBr),2);
swappedPixels = sum(mask); %// Get the count of "swappings"

%// Perform the "swaps" and then reshape FI back to its original size
FIr(mask,:) = BIr(mask,:);
FI = reshape(FIr,size(FI));

Бенчмаркинг

В этом разделе рассматривается сравнительный анализ предлагаемого векторизационного кода с исходным кодом. Для начала, мы создали функции форм исходного и векторизованного кодов.

(1) исходный код -

function [FI,swappedPixels] = org_code(FI,BI,CommonRGB)

refRows =size(FI,1);
refCols =size(FI,2);

swappedPixels = 0;
for row=1:refRows
    for col=1:refCols
        if(FI(row,col,:)==CommonRGB)
            FI(row,col,:)=BI(row,col,:);
            swappedPixels = swappedPixels + 1;
        end
    end
end

return;

(2) Векторизованный код -

function [FI,swappedPixels] = vectorized_code(FI,BI,CommonRGB)

FIr = reshape(FI,[],3);
BIr = reshape(BI,[],3);
CommonRGBr = reshape(CommonRGB,[],3);

mask = all(bsxfun(@eq,FIr,CommonRGBr),2);
swappedPixels = sum(mask);

FIr(mask,:) = BIr(mask,:);
FI = reshape(FIr,size(FI));

return;

(3) Наконец, вот код бенчмаркинга, который также строит графики ускорений -

N_arr = [100 200 500 1000 2000 4000]; %// datasize array
timeall = zeros(2,numel(N_arr));
for iter = 1:numel(N_arr)
    N = N_arr(iter);
    FI = uint8(randi(255,N,N,3));
    BI = uint8(randi(255,N,N,3));

    CommonRGB = mode(mode(FI));
    BI = imresize(BI, [size(FI,1) size(FI,2)]); 

    f = @() org_code(FI,BI,CommonRGB);
    timeall(1,iter) = timeit(f);
    clear f

    f = @() vectorized_code(FI,BI,CommonRGB);
    timeall(2,iter) = timeit(f);
    clear f
end
figure,hold on,grid on
plot(N_arr,timeall(1,:)./timeall(2,:),'-bo')
legend('Speedup with Vectorized Code over Original one'),
xlabel('Datasize ->'),ylabel('Speedup Factor (x)')

Результат построения, полученный в моей системе, выглядел следующим образом -

Введите описание изображения здесь

Выводы

Это предполагает колоссальное ускорение, близкое к 170x с предлагаемым векторизованным решением! Надеясь, что это несколько заманит вас в бизнес векторизации и bsxfun в частности.


Конфигурация Системы

MATLAB Version: 8.3.0.532 (R2014a)
Operating System: Windows 7
RAM: 3GB
CPU Model: Intel® Pentium® Processor E5400 (2M Cache, 2.70 GHz)

Вы правильно предположили, что логическое индексирование - это путь, по которому нужно идти. Вы можете выбрать пиксели, удовлетворяющие условию в массиве, и использовать результат для индексации:

В случае FI и BI, где изображения в оттенках серого:

FI(FI==value)=BI(FI==value);

Что здесь происходит: FI==value генерирует двоичную матрицу размером FI с 1 (true) в позициях, где выполняется условие, и 0 (false) везде. Эта двоичная матрица затем может быть использована для доступа к соответствующим элементы в FI или любой другой матрице того же размера. Здесь элементы FI, удовлетворяющие условию, заменяются элементами BI, которые находятся в тех же позициях.


Тот же подход может быть использован для изображений RGB, если вы создадите трехмерный массив, копирующий двоичную маску вдоль 3-го измерения, например, используя repmat.

Mask=(FI(:,:,1)==CommonRGB(1,1,1)).*(FI(:,:,2)==CommonRGB(1,1,2)).*(FI(:,:,3)==CommonRGB(1,1,3));
MaskRGB=repmat(Mask, 1, 1, 3);

FI(MaskRGB)=BI(MaskRBG);

Обратите внимание, что .* - это пиксельное умножение, оно идентично логическому и. Смотрите также это . Итак, первая строка создает двоичную маску, в которой условие выполняется во всех 3 слоях изображения RGB.

Если вам нужно количество обмененных пикселей, используйте:

swappedPixels = nnz(Mask);

Из того, что я понял здесь, есть способ, которым вы могли бы это сделать.

Вместо оператора if вы можете использовать find , чтобы найти пиксели в каждом канале FI, которые подобны наиболее распространенному значению для этого конкретного канала (отсюда 3-е измерение commonRGB). После того, как у вас есть строки и столбцы этих пикселей, его легко и быстро использовать логическое индексирование для замены пикселей из BI в FI. Обратите внимание, что я использовал тестовые изображения, которые поставляются с Matlab, поэтому выходные данные могут не быть представитель чего угодно, но я думаю, что он делает то, что вы хотите. Если нет, пожалуйста, дайте мне знать!

Вот комментируемый код:

clear
clc
close all

FI1 = imread('peppers.png');
BI = imread('pears.png');

FI = FI1; %// Only used to display original image

[refRows,refCols,refChan] = size(FI); %// 1-liner for assignments.

CommonRGB = squeeze(mode(mode(FI)));

BI = imresize(BI, [refRows refCols]);

swappedPixels = zeros(1,3); %// We will use it differently than in your code.

%// Loop through each channel and use find to locate pixels in FI that are
%// similar to commonRGB
for CheckChannel = 1:refChan

    [row,col] = find(FI(:,:,CheckChannel) == CommonRGB(CheckChannel));

    FI(row,col,CheckChannel) = BI(row,col,CheckChannel); %// Index pixel value from BI into FI.

    swappedPixels(CheckChannel) = numel(row); %// Calculate the number of elements in the vector, i.e. number of swapped pixels.
end

 swappedPixels %// To see the number of swapped pixels

%// Display results.
figure;

subplot(1,3,1)
imshow(FI1);
title('Original foreground image','FontSize',16);

subplot(1,3,2)
imshow(BI);
title('Original background image','FontSize',16);

subplot(1,3,3)
imshow(FI);
title('Modified foreground','FontSize',16);

Массив swappedPixels выглядит следующим образом:

swappedPixels =

       11175        4508       10753
Где каждое число соответствует одному из 3-х измерений изображений.

Выходные изображения выглядят следующим образом:

Введите описание изображения здесь

Я бы решил эту проблему таким образом.

  refRows =size(fi,1);
  refCols =size(fi,2);
  CommonRGB = mode(mode(FI));
  % finding where the 3 channels are equals
  [indicesX indicesY ] = find(fi(:,:,1)==CommonRGB(1) & 
                              fi(:,:,2)== CommonRGB(2) & 
                              fi(:,:,3)== CommonRGB(3))


  for c=1:3
       %find the linear index for 3 channels and replace
       idx = sub2ind([refRows ,refCols ,3], indicesX, indicesY,[c c]')
       fi(idx ) = bi(idx );
  end

  %counting swapped pixels
  swappedPixels = length(indicesX);

Comments

    Ничего не найдено.