Splitting a color image into sub-images only displays the yellow color
3 views (last 30 days)
Show older comments
I'm trying to create my own datasets for testing stitching algorithms by splitting images into sub-images. The script I wrote succesfully extracts the relevant rgb values from the correct pixels that make up a certain sub-image, but when I try to display the image I get only a yellow color. (I'm aware this code doesn't take edge cases into account)
Am I switching up the rgb values somewhere or am I using the wrong function to display the subimage? I'm out of ideas.
This is the original picture I used:
This is the first 600 x 400 pixel sub-image.
I = imread("AnyImage");
imshow(I)
I(1,2,:)
[names, images] = split(I, 10, 10);
IS = squeeze(images(1,2,:,:,:));
size(IS)
IS(1,2,:)
image(IS)
% SPLIT_IMAGE splits an image into a nrows by ncolumns grid of sub-images.
function [names, images] = split(image, nrows, ncolumns, ~)
[rows, columns, ~] = size(image);
subimage_height = round(rows/nrows);
subimage_width = round(columns/ncolumns);
subimage_names = strings(nrows, ncolumns);
subimages = zeros(nrows, ncolumns, subimage_height, subimage_width, 3);
for r = 1:nrows
for c = 1:ncolumns
formatspec = "subimage_%s";
A = [int2str(r) int2str(c)];
subimage_names(r, c) = sprintf(formatspec, A);
subimages(r, c, :, :, :) = image((r-1)*subimage_height+1:r*subimage_height, ...
(c-1)*subimage_width+1:c*subimage_width, :);
end
end
names = subimage_names;
images = subimages;
end
0 Comments
Accepted Answer
DGM
on 7 Dec 2022
Edited: DGM
on 7 Dec 2022
This is an issue of data class and scale. You're (implicitly) preallocating your output to a floating point class. Your input data is uint8. You end up populating the output array with data that's scaled improperly for its class. It's not (necessarily) destructive to handle images that are improperly-scaled floating point, but tools like imshow() and imwrite() won't know what to do with them.
Floating point images are expected to be unit scale ([0 1]). You're feeding imshow() an image that's scaled to [0 255]. As a result, almost everything is whiter than white.
The simple way to fix this:
subimages = zeros(nrows, ncolumns, subimage_height, subimage_width, 3, class(image));
Also, naming things "image" isn't good practice, since it's not descriptive, and it shadows the function image(). That said, it's not going to hurt anything here.
For what it's worth, I'd do the splitting like this:
inpict = imread('peppers.png'); % a 384x512x3 image
% use MIMT imdetile()
tiling = [10 10]; % [y x]
outpict = imdetile(inpict,tiling,'direction','col'); % detile to 38x51x3x100
% if you don't like the fact that the tile indexing is 1-dimensional
% you can shove the 4D stack into a cell array and reshape it
outpict = squeeze(num2cell(outpict,[1 2 3]));
outpict = reshape(outpict,tiling) % a 10x10 cell of 38x51x3 images
% ... save the files or something
At this point, you can save the tiles in a loop or whatever. Unless you want to operate on the entire array at once, it's often better to use cell arrays for this sort of thing, as it allows for some better memory management under the hood.
Unlike most mat2cell()-based approaches that get recommended, MIMT imdetile() will detile any image regardless of whether it's integer-divisible by the tiling.
2 Comments
DGM
on 7 Dec 2022
Edited: DGM
on 7 Dec 2022
Yeah, if you're going to do overlapping tiles, imdetile() won't help. The tool that makes imdetile() work is maketileable(), but that won't really work either.
Bear in mind that there is currently a minor bugfix in maketileable() that hasn't been published yet. It shouldn't be of consequence here, but if you find the internals to be confusing, that might be why.
You already have your thoughts invested in the code you're writing, so I don't know how much headaches you'll save by trying to adapt my ideas.
More Answers (1)
Constantino Carlos Reyes-Aldasoro
on 6 Dec 2022
From the images it seems that your channel (the yellow one) is saturated. Have you tried to display the three RGB channels is separate subplots? I.e.
subplot(221); imshow(I)
subplot(222); imshow(I(:,:,1))
subplot(223); imshow(I(:,:,2))
subplot(224); imshow(I(:,:,3))
That is assuming of course that I has dimensions like (r,c,3) for the number of rows, columns and 3 for RGB. Another thing you can do to find out more of the nature of the yellow image is to add a colorbar, that will show you the range of values you are working with.
Hope this helps
5 Comments
Constantino Carlos Reyes-Aldasoro
on 7 Dec 2022
I think that we are going back to the main question, that is what is what you really need? Just display and visualise the three RGB channels? Or you need something else. If it is just about things "looking" right, then there are many ways to solve this, for instance
Let's create an image with only a saturated red channel:
a=zeros(64,64,3);
a(:,:,1) = 50+300*rand(64,64);
imshow(a)
As mentioned by DGM, you also have to be aware of the data types you are working with, these were doubles, so you can "see" this better if you divide by the maximum and use imagesc instead of imshow:
imagesc(a/max(a(:)))
or even converting the doubles to uints would help if you want to keep imshow
imshow(uint8(a))
What data types are you working with? use "whos" in the command line and that will help. Also look for the maximum values of your data:
max_a = max(a(:))
If you are using uint8, then the maximum value before saturation will be 255 so you can normalise your data to have a maximum of 255
a2 = uint8(255*a/max_a);
imshow(a2)
And now the problem of saturation is solved.
Hope that this has given you enough information to solve your problem.
See Also
Categories
Find more on Image Processing Toolbox in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!