extracting contents and calculating area within an image - matlab

Objective :
I need to extract and highlight a specific color from a given image[Say X] and finally I need to calculate the Area of X.
I need help in :
Detecting individual labels in the image[below] in form of a circle.
What I have done :
Below is my code and I have attached the RGB variable's image[below]. I have extracted the "beige color" from the given image(that is X) and labeled it.
To detect the individual component, I tried like the classical "Coins Example [Identifying different coin values from an image using MATLAB", to first detect element. But here as the regions are not uniform, So, I am bit confused.
Code :
clear all; clc;
close all;
I = imread('19.jpg');
%figure;imshow(I);
B = White_Beige(I); // The Function is used to threshold the RGB image
to LAB colorspace. Here, we extract the Beige
color present in the image and provide the final
Black and White image with the presence of Beige
color as Black and rest as white.
%figure; imshow(B);
BW2 = imfill(~B,'holes');
figure; imshow(BW2);
Border = imclearborder(BW2);
[OBL,nbOBObjets] = bwlabel(Border,8);
RGB = label2rgb(OBL,'jet',[0 0 0]);
figure, imshow(RGB);
Any help is much appreciated

maybe this will help:
Note that in this code the "area" occupied by a color is represented as a real number on the interval (0, 1), where 0 means no pixel in the image is that color, and 1 means that all of the pixels in the image are that color.
[A, map] = imread('L8IZU_2.png');
image(A); % display the image
if ~isempty(map)
A = ind2rgb(X, map); % convert image to RGB
end
color_counts = zeros(256, 256, 256);
% color_counts(x, y, z) records the number of pixels in the image
% which have RGB color [x-1, y-1, z-1];
% For example color_counts(1, 1, 1) records the number of pixels in
% the image which have color [0, 0, 0];
% As another example, color_counts(65, 43, 11) records the number of
% pixels in the image which are of color [64, 42, 10];
time_of_last_progress_report = clock;
% traverse the image pixel by pixel
for m = 1:size(A, 1)
for n = 1:size(A, 2)
current_time = clock;
if etime(current_time, time_of_last_progress_report) > 2
percent_progress = 100*(((m - 1)*size(A, 2) + n)/(size(A, 1)*size(A, 2)));
disp(strcat(num2str(percent_progress),'% of the image has been scanned'));
time_of_last_progress_report = clock;
end
current_color = A(m, n, :);
current_color = squeeze(current_color);
% squeeze removes singleton dimensions
% in other words, squeeze converts current_color from a 1x1x3 array into
% a 3x1 array
index = current_color + uint8(ones(size(current_color)));
% Let us say that index = [1, 3, 4];
% We want to increment color_counts(1, 3, 4);
% However, color_counts([1, 3, 4]), or color_counts(index),
% does not return a reference to one single element at that location
% Instead, color_counts([1, 3, 4]) returns a 1x3 vector containing
% the 1st, 3rd, and 4th elements of the color_counts array
% we convert index into a cell array and then convert the cell
% array into a comma separated list.
index2 = num2cell(index);
old_count = color_counts(index2{:});
color_counts(index2{:}) = 1 + old_count;
end
end
disp(' ');% line break
disp('Now sorting colors by how often they apeared....');
color_counts_linear = reshape(color_counts, 1, numel(color_counts));
[color_counts_sorted, indicies] = sort(color_counts_linear, 'descend');
[R,G,B] = ind2sub(size(color_counts),indicies);
R = R - ones(size(R));
G = G - ones(size(G));
B = B - ones(size(B));
areas = (1/(size(A, 1)*size(A, 2))) * color_counts_sorted;
% display the top 5 most common colors
disp(' '); % line break
disp('Top 5 most common colors:');
for k = 1:min(5, numel(areas))
disp(strcat('[', num2str(R(k)), ',', num2str(B(k)), ',', num2str(G(k)),'] : ', num2str(100*areas(k)), '%'));
end

Related

How to convert a 3D array directly to a video in matlab

I have a 3D matrix with a dimension of 600x600x28 which the last index is the number of frames and want to convert it to a video file. As you can see in the code below I convert the array into a 4D matrix with the size of 600X600X3X28 to make it compatible with WriteVideo format. but I am not sure why the output video is empty!
Orig = randi([1 1000],600,600,28);
x = uint8(255 * mat2gray(Orig));
map=jet;
for i=1:size(x,3)
x_all(:,:,:,i) = ind2rgb(x(:,:,i),map);
end
x_all = uint8(x_all);
v = VideoWriter('myvideo');
open(v);
writeVideo(v,x_all);
close(v)
You don't have to convert the video to RGB, you may create a Grayscale video.
Example:
x = randi([0, 255], 600, 600, 28, 'uint8');
v = VideoWriter('myvideo_grayscale.avi', 'Grayscale AVI');
open(v);
writeVideo(v, x);
close(v)
The reason you are getting a black video is that ind2rgb returns values in range [0, 1] and when converting to uint8: x_all = uint8(x_all);, all the values are 0s and 1s (instead of range [0, 255]).
Example for creating colored video:
Orig = randi([1 1000],600,600,28);
x = im2uint8(mat2gray(Orig));
map = jet(256); % Use colormap with 256 colors (the default is 64).
x_all = zeros(size(x, 1), size(x, 2), 3, size(x, 3), 'uint8'); % Allocate space
for i=1:size(x,3)
x_all(:,:,:,i) = im2uint8(ind2rgb(x(:,:,i), map));
end
v = VideoWriter('myvideo.avi');
open(v);
writeVideo(v,x_all);
close(v)
In case you are actually getting an empty video file (not just black video), something is wrong with your setup.

How to find and highlight the brightest region an image in Matlab?

Dears,
I would like to kindly ask you for support. My goal is to find the brightest region of the RGB image and highlight it without additional tools. Please see my example below.
rgbImage = imread( 'Zoom1_WhiteImage.png' );
imshow(rgbImage);
[rows, columns, numberOfColorChannels] = size(rgbImage)
[x, y] = meshgrid(1:columns, 1:rows);
% Extract the individual red, green, and blue color channels.
% Need to cast to double or else x and y will be clipped to 255 when we concatenate them.
if numberOfColorChannels == 1
% Leave as gray scale.
% Get array listing [r, g, b, x, y]. Using (:) will turn all the 2-D arrays into column vectors.
output = [rgbImage(:), x(:), y(:)];
else
redChannel = double(rgbImage(:, :, 1));
greenChannel = double(rgbImage(:, :, 2));
blueChannel = double(rgbImage(:, :, 3));
% Get array listing [r, g, b, x, y]. Using (:) will turn all the 2-D arrays into column vectors.
output = [redChannel(:), greenChannel(:), blueChannel(:), x(:), y(:)];
end
[rows, columns] = find(rgbImage == 155);
imshow(rgbImage);
hold on
Unfortunately, I am struggling with how to proceed with the plot of the points that should overlay the grey image.
Would you be so kind and help me to finish the code, please?
I'd recommend you read about logical indexing in MATLAB - it's a very powerful concept, and it allows you to skip most of the things you're trying to do by flattening the arrays, and creating separate arrays of indices with meshgrid. Here is an article that addresses logical indexing down at the bottom, for example.
I've modified and added to your code so it accomplishes the job using logical indexing. I tested this in R2019b.
rgbImage = imread( 'Zoom1_WhiteImage.png' );
imshow(rgbImage);
[rows, columns, numberOfColorChannels] = size(rgbImage)
% This is unnecessary - the 'find' command will generate the indices
% without you preparing a matrix of them:
% [x, y] = meshgrid(1:columns, 1:rows);
if numberOfColorChannels == 1
% No need to flatten the image, or combine it with indices:
%output = [rgbImage(:), x(:), y(:)];
brightness = rgbImage; % For a 1 channel image, brightness is the same as the original pixel value.
else
% redChannel = double(rgbImage(:, :, 1));
% greenChannel = double(rgbImage(:, :, 2));
% blueChannel = double(rgbImage(:, :, 3));
% Get array listing [r, g, b, x, y]. Using (:) will turn all the 2-D arrays into column vectors.
% output = [redChannel(:), greenChannel(:), blueChannel(:), x(:), y(:)];
% For an RGB image, the brightness can be estimated in various ways, here's one standard formula:
brightness = (0.2126*rgbImage(:, :, 1) + 0.7152*rgbImage(:, :, 2) + 0.0722*rgbImage(:, :, 3));
end
% Establish a brightness threshold - pixels brighter than this value will
% be highlighted
threshold = 215;
% Create a zero-filled mask of equal size to the image to hold the
% information of which pixels met the brightness criterion:
mask = zeros(rows, columns, 'logical');
% Assign "1" to any mask pixels whose corresponding image pixel met the
% brightness criterion:
mask(brightness > threshold) = 1;
figure;
% Overlay and display mask. imoverlay was introduced in R2017, but the
% syntax has changed a bit, so check the version you're using. You can also
% use imfuse, or imshowpair, or make your own image blending algorithm,
% depending on how you want it to look.
imshow(imoverlay(rgbImage, mask, 'red'));
hold on

Chessboard distance in the image matrix

Given an image matrix, how can I get the locations of the pixels whose chessboard distances from pixel A is less than D. I need to performed this for all pixels.
Using the MATLAB function bwdist I couldn't provide desired result. What's the solution?
[D,idx] = bwdist(Img,'chessboard');
Given an image, pixel and maximum distance:
% Test image
Image = zeros(20,30);
% Maximum chessboard distance from image
maxDist = 7;
% The pixel from which to measure distance
pix = [4,19];
To find the pixels who's chessboard distance from the pix are
less than maxDist and in the image bounds:
Option 1: Using bwdist
% Create a binary image with all pixels zero except 'pix'
bw = zeros(size(Image));
bw(pix(1), pix(2)) = 1;
% Get the chessboard distance transform
[D,idx] = bwdist(bw,'chessboard');
% Get the linear index of 'pix'
pixInd = sub2ind(size(bw), pix(1), pix(2));
% Find linear indices of pixels who's chessboard distance from pixel are
% less than 'maxDist'
pointsInd = find(idx == pixInd & D < maxDist);
% Remove 'pix'
pointsInd(pointsInd == pixInd) = [];
% Get the pairs of (x,y) of the pixels
[pointsX, pointsY] = ind2sub(size(bw), pointsInd);
Option 2: Using meshgrid
% Get the range of x and y indices who's chessboard distance from pixel are
% less than 'maxDist' and in the image bounds
xRange = max((pix(1)-(maxDist-1)),1):min((pix(1)+(maxDist-1)),size(Image,1));
yRange = max((pix(2)-(maxDist-1)),1):min((pix(2)+(maxDist-1)),size(Image,2));
% Create a mesgrid to get the pairs of (x,y) of the pixels
[pointsX, pointsY] = meshgrid(xRange, yRange);
pointsX = pointsX(:);
pointsY = pointsY(:);
% Remove 'pix'
pixIndToRemove = (pointsX == pix(1) & pointsY == pix(2));
pointsX(pixIndToRemove) = [];
pointsY(pixIndToRemove) = [];
Displaying result:
% Get linear indices of pixels
pointsInd = sub2ind(size(Image), pointsX, pointsY);
% To display the result, create a binary image with all found pixels
% colored white
bwPoints = zeros(size(Image));
bwPoints(pointsInd) = 1;
% Show points
imshow(bwPoints, 'InitialMagnification', 2000)
% Show pixel grid lines
hold on
[rows, cols] = size(bwPoints);
for row = 0.5 : 1 : (rows + 0.5)
line([0.5, cols+0.5], [row, row], 'Color', 'r', 'LineWidth', 0.5);
end
for col = 0.5 : 1 : (cols + 0.5)
line([col, col], [0.5, rows+0.5], 'Color', 'r', 'LineWidth', 0.5);
end
Efficiency and running in a loop over all image pixels:
Option 2 is way much faster than Option 1. I wrote first Option 1 because bwdist was mentioned in the question. Running Option 2 in a loop can be improved by calculating the pixels first and than shifting them to the location of each pixel:
% Get the range of x and y indices who's chessboard distance from pixel
% (0,0) are less than 'maxDist'
xRange = (-(maxDist-1)):(maxDist-1);
yRange = (-(maxDist-1)):(maxDist-1);
% Create a mesgrid to get the pairs of (x,y) of the pixels
[pointsX, pointsY] = meshgrid(xRange, yRange);
pointsX = pointsX(:);
pointsY = pointsY(:);
% Remove pixel (0,0)
pixIndToRemove = (pointsX == 0 & pointsY == 0);
pointsX(pixIndToRemove) = [];
pointsY(pixIndToRemove) = [];
for x=1:size(Image, 1)
for y=1:size(Image, 2)
% Get a shifted copy of 'pointsX' and 'pointsY' that is centered
% around (x, y)
pointsX1 = pointsX + x;
pointsY1 = pointsY + y;
% Remove the the pixels that are out of the image bounds
inBounds =...
pointsX1 >= 1 & pointsX1 <= size(Image, 1) &...
pointsY1 >= 1 & pointsY1 <= size(Image, 2);
pointsX1 = pointsX1(inBounds);
pointsY1 = pointsY1(inBounds);
% Do stuff with 'pointsX1' and 'pointsY1'
% ...
end
end
"The aim is to access the location of the pixels whose chessboard distances from pixel A is less than D. The process should be
performed for all pixels..."
Since D is creating a square selection area, just use simple maths..
For example: if D is 3 then from the [x,y] position of pixel A...
//# we minus D by 1 since you want less than D (not equal / higher)
Start-X = pixelA.x - (D-1); //from the left
End-X = pixelA.y + (D-1); //to the right
Start-Y = pixelA.y - (D-1); //from the top
End-Y = pixelA.y + (D-1); //to the bottom
That will give you a square perimeter that represents your required selection area.
Look at this example image below:
Each square is a pixel. If the "crown" icon represents pixel A and D is 3 (where your "less than D" means D has a maximum length of 2 pixels), can you see how the pseudo-code above applies?

Assigning color to points in the three dimensional space depending on the functional values

I was thinking of a question similar to this.
I have a function which takes as input the three values x,y,z from the R^3 and returns either 1,2,3,4. Now I wanted to plot the point in the 3D space with coordinates (x,y,z) with a color associated with the functional value at that point which can be either one of 1,2,3 or 4.
I have a 3D matrix with integer entries like say 1,2,3,4 and I store the points value in this matrix so that I can plot the points with the corresponding color (similar trick of 'image' command in MATLAB for making 2D plots).
color coding (say)-
1 - green, 2 - blue , 3 - cyan , 4 -red
Like if at the point (0.5,0.5,0.1) the function returns the value 3, then I mark the point (0.5,0.5,0.1) with the color associated to number three which is cyan.
I am thinking of a MATLAB command which does this in the case of three dimensional case as the "image" command seems to work for the 2D case.
I can only think of some kind of workaround, like this:
% Input: A = coordinates, b = functional values.
A = rand(20, 3);
b = ceil(rand(20, 1) * 4);
% Color map.
cm = [0 1 0; 0 0 1; 0 1 1; 1 0 0];
% Circle size.
cs = 21;
% 3D scatter plot.
figure(1);
hold on;
for k = 1:size(cm, 1)
idx = (b == k);
scatter3(A(idx, 1), A(idx, 2), A(idx, 3), cs, cm(k, :), 'filled');
end
hold off;
view(45, 30);
grid on;
Gives the following output:
You can linearize the solution suggested by #HansHirse, so a small improvement could be:
% Dummy data
A = rand(20, 3);
b = ceil(rand(20, 1) * 4);
% color vector
c = [0 1 0; 0 0 1; 0 1 1; 1 0 0];
% Use the linear indexing to select the right color
scatter3(A(:,1),A(:,2),A(:,3),[],c(b,:),"filled")
Even simpler you can just use b as color input and matlab will use the default colormap to set the color according to b
scatter3(A(:,1),A(:,2),A(:,3),[],b,"filled")

Matlab image translation matrix

I'm very new to Matlab. I'm learning some image manipulation basics, and I'm a bit confused on how to write a translation without using imtranslate.
this is my code but it just displays a black background. Thank you.
img = imread('name2.png');
figure(1);
% pixel matrix
[orig_x, orig_y,z] = size(img);
final_x = 600;
final_y = 600;
% define the final array with calculated dimensions and fill the array with zeros ie.,black
final_img = uint8(zeros([final_x final_y 3 ]));
for i = 1 : size(final_img, 1)
for j = 1 : size(final_img, 2)
new_x = img(i) + 5;
new_y = img(j) + 5;
% fprintf('X: %f\n',new_x); % prints 255
final_img(i) = new_x;
final_img(j) = new_y;
end
end
imshow(final_img);
This is one solution for 'translation only' transformation.
I = imread('Lenna.png');
shiftX = 5; % shift columns
shiftY = 5; % shift rows
% Assigning empty matrix for result, expected to be shiftX-1 larger in rows and shiftY-1 larger in columns
nI = uint8( zeros(size(I,1)+shiftY-1, size(I,2)+shiftX-1, size(I,3));
% Translate
nI(shiftY:end, shiftX:end, :) = I;
imshow(nI)
Now the image will start from (x,y) = (5,5) instead of (1,1). Also note that in matlab image coordinate system, x and y axis start from upper left corner (documentation).
final_img:
You've defined "final_img" with new x and new y but you haven't replaced the zeros in the red/green/blue values. It's all black because of your initialisation filling the final_img with all zeros.
Maybe try this instead of what you've written:
%{
[X,map] = imread('name2.png');
figure(1);
% X should be 600 by 600
%Translate X however you wish, e.g.:
X = X +5;
%Verify that the colormap, map, is not empty, and convert
%the data in X to RGB and store as your final_img.
if ~isempty(map)
final_img = ind2rgb(X,map);
end
%}
I am also not sure if you want to be indexing img with just a single i without the the other dimensions like you have:
new_x = img(i) + 5;
?
For the problems in your specific code, I wrote in the comments some of them.
A short way to achieve image translation is by 2D convolution with a filter of zeros and just one 1, that will preserve the values of the image, but relocate them according to the size of the filter and the position of the 1 in it.
That seems you want to move the image but preserve the size of the total image, if I get it right. So:
r=3; c=5; % number of rows and columns to move
filt=zeros(r*2+1, c*2+1); filt(end)=1; % the filetr
img2=conv2(img,filt,'same'); % the translated image
Just for the example, lets translate "cameraman" with 20 rows and columns:
img=imread('cameraman.tif');
imshow(img)
r=20; c=20;
filt=zeros(r*2+1, c*2+1); filt(end)=1;
img2=conv2(img,filt,'same');
figure; imshow(img2,[])