How best to change the colors of pixels in an image with known X (column) Y (row) coordinates?

1 view (last 30 days)
The obvious way is to loop over every XY pair and change the color of the identified pixel, but this is very slow. I can do a lot better using indexing, but it is a clumsy approach, as it does the same thing three times. Is there a better, more intuitive way (even if it's not much faster)?
Image = uint8(repmat(randi(100, [9, 9]), [1, 1, 3]));
Image2 = Image;
Image3 = Image;
Color = uint8([1, 0.25, 0.1] .* 255);
Xs = [2; 2; 3; 3; 3; 4; 5; 6; 7; 7; 7; 8; 8];
Ys = [3; 6; 2; 3; 7; 8; 8; 8; 2; 3; 7; 3; 6];
% Adding lots of duplicates to make this simple problem harder.
Xs = repmat(Xs, [1000000, 1]);
Ys = repmat(Ys, [1000000, 1]);
% Looping is slow.
tic
for ii = 1:numel(Xs)
Image(Ys(ii), Xs(ii), :) = Color;
end
t1 = toc;
% The best I could come up with. It's faster, but inelegant.
tic
idx = sub2ind(size(Image2, [1, 2]), Ys, Xs);
Image2(idx) = Color(1);
Image2(idx + prod(size(Image3, [1, 2]))) = Color(2);
Image2(idx + 2.*prod(size(Image3, [1, 2]))) = Color(3);
t2 = toc;
% Is there a better way?
tic
% Do something...
t3 = toc;
montage({Image, Image2, Image3}, 'Size', [1, 3])
disp(['Loop time = ', num2str(t1, 3), ' seconds.'])
disp(['Index time = ', num2str(t2, 2), ' seconds.'])
disp(['New time = ', num2str(t3, 2), ' seconds.'])
disp(['Indexing is ~', num2str(round(t1./t2), '%d'), ' times faster than looping.'])

Accepted Answer

DGM
DGM on 16 Feb 2023
Edited: DGM on 16 Feb 2023
Normally, you'd do region replacement using a mask of some sort.
Image = uint8(repmat(randi(100, [9, 9]), [1, 1, 3]));
Image2 = Image;
Image3 = Image;
Color = uint8([1, 0.25, 0.1] .* 255);
Xs = [2; 2; 3; 3; 3; 4; 5; 6; 7; 7; 7; 8; 8];
Ys = [3; 6; 2; 3; 7; 8; 8; 8; 2; 3; 7; 3; 6];
% Adding lots of duplicates to make this simple problem harder.
Xs = repmat(Xs, [1000000, 1]);
Ys = repmat(Ys, [1000000, 1]);
% Looping is slow.
tic
for ii = 1:numel(Xs)
Image(Ys(ii), Xs(ii), :) = Color;
end
t1 = toc;
% The best I could come up with. It's faster, but inelegant.
tic
idx = sub2ind(size(Image2, [1, 2]), Ys, Xs);
Image2(idx) = Color(1);
Image2(idx + prod(size(Image3, [1, 2]))) = Color(2);
Image2(idx + 2.*prod(size(Image3, [1, 2]))) = Color(3);
t2 = toc;
% use a mask
tic
sz = size(Image2, [1, 2]);
idx = sub2ind(sz, Ys, Xs);
mask = false(sz);
mask(idx) = true;
% IPT imoverlay() can work with I/RGB images
% BG must be an image, FG must be an RGB tuple
% mask can only be binarized
% and FG color must be unit-scale float
Image3 = imoverlay(Image3,mask,im2double(Color));
% MIMT replacepixels() can work with I/IA/RGB/RGBA/RGBAAA images
% FG/BG can be images or tuples
% mask can be binarized or smooth, I/RGB
% color can be anything so long as it's correctly-scaled for its class
%Image3 = replacepixels(Color,Image3,mask);
t3 = toc;
montage({Image, Image2, Image3}, 'Size', [1, 3])
disp(['Loop time = ', num2str(t1, 3), ' seconds.'])
Loop time = 4.73 seconds.
disp(['Index time = ', num2str(t2, 2), ' seconds.'])
Index time = 0.33 seconds.
disp(['New time = ', num2str(t3, 2), ' seconds.'])
New time = 0.29 seconds.
disp(['Indexing is ~', num2str(round(t1./t2), '%d'), ' times faster than looping.'])
Indexing is ~14 times faster than looping.
disp(['Masking is ~', num2str(round(t1./t3), '%d'), ' times faster than looping.'])
Masking is ~16 times faster than looping.
If all you need are binary selections and you're replacing the regions with a single color, then IPT imoverlay() works. If the selected regions are to be replaced by another image, then imoverlay() won't work.
Filling with another image, using only basic tools:
Filling with a solid fill, using basic tools or using MIMT replacepixels():

More Answers (0)

Categories

Find more on Images in Help Center and File Exchange

Products


Release

R2022b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!