Векторизация в среде 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 означает, что он будет использовать его, а ноль означает, что он не будет смотреть на это значение.
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выглядит следующим образом:Где каждое число соответствует одному из 3-х измерений изображений.swappedPixels = 11175 4508 10753Выходные изображения выглядят следующим образом:
Я бы решил эту проблему таким образом.
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