Matlab get vector of specific pixels - matlab

I am pretty new to Matlab and encountered a problem when working with images.
I want to get a pixel that is in a specific colour (blue) in the following image:
image
My current code looks something like this:
function p = mark(image)
%// display image I in figure
imshow(image);
%// first detect all blue values higher 60
high_blue = find(image(:,:,3)>60);
%cross elements is needed as an array later on, have to initialize it with 0
cross_elements = 0;
%// in this iteration the marked values are reduced to the ones
%where the statement R+G < B+70 applies
for i = 1:length(high_blue)
%// my image has the size 1024*768, so to access the red/green/blue values
%// i have to call the i-th, i+1024*768-th or i+1024*768*2-th position of the "array"
if ((image(high_blue(i))+image(high_blue(i)+768*1024))<...
image(high_blue(i)+2*768*1024)+70)
%add it to the array
cross_elements(end+1) = high_blue(i);
end
end
%// delete the zero element, it was only needed as a filler
cross_elements = cross_elements(cross_elements~=0);
high_vector = zeros(length(cross_elements),2);
for i = 1:length(cross_elements)
high_vector(i,1) = ceil(cross_elements(i)/768);
high_vector(i,2) = mod(cross_elements(i), 768);
end
black = zeros(768 ,1024);
for i = 1:length(high_vector)
black(high_vector(i,2), high_vector(i,1)) = 1;
end
cc = bwconncomp(black);
a = regionprops(cc, 'Centroid');
p = cat(1, a.Centroid);
%// considering the detection of the crosses:
%// RGB with B>100, R+G < 100 for B<150
%// consider detection in HSV?
%// close the figure
%// find(I(:,:,3)>150)
close;
end
but it is not optimized for Matlab, obviously.
So i was wondering if there was a way to search for pixels with specific values,
where the blue value is larger than 60 (not hard with the find command,
but at the same time the values in the red and green area not too high.
Is there a command I am missing?
Since English isn't my native language, it might even help if you gave me some suitable keywords for googling ;)
Thanks in advance

Based on your question at the end of the code, you could get what you want in a single line:
NewImage = OldImage(:,:,1) < SomeValue & OldImage(:,:,2) < SomeValue & OldImage(:,:,3) > 60;
imshow(NewImage);
for example, where as you see you provide a restriction for each channel using logical operators, that you can customize of course (eg. using | as logical OR). Is this what you are looking for? According to your code you seem to be looking for specific regions in the image like crosses or coins is that the case? Please provide more details if the code I gave you is completely off the track :)
Simple example:
A = imread('peppers.png');
B = A(:,:,3)>60 & A(:,:,2)<150 & A(:,:,1) < 100;
figure;
subplot(1,2,1);
imshow(A);
subplot(1,2,2)
imshow(B);
Giving this:

Related

Why my segmentation using HSV space isn't giving me a good result?

I am attending a class on image processing with Matlab and I found a problem on the internet and I tried to solve it but it is hard, because I am new to this subject.
The problem has to be solved with Matlab and it is new for me as well.
Problem:
'Think of RGB and HSV spaces as 3-dimensional spaces. Define a
neighborhood distance (we can use ecludien distance) and set to 0 all
the pixels whose value in RGB or HSV space is far (in the sense of
this distance) from your reference value. See if you can easily
achieve the same type of segmentation in both cases. Try for example
to segment only the helmets of the warriors of the image
bilibinWar.jpg.'
I tried to code it but i don't know if the result is good.
%%
% 4) 8 - Segmentatiion dans l’’espace HSV et RVB..
clear all; close all;
ImagebilibinWar = imread("bilibinWar.jpg");
[lin, col, pla] = size(ImagebilibinWar);
figure(1);imshow(ImagebilibinWar);
Image_bilibinWar_HSV= rgb2hsv(ImagebilibinWar);
[lo,co] = ginput(1);
lo = round(lo);
co = round(co);
RIOH = Image_bilibinWar_HSV(lo,co,1);
%%
% Im going to use this first section so ican isolate the helmets inside a circle ARROUND THE HELMETS and get rid of REST background Circle_Image_bilibinWar_HSV = Image_bilibinWar_HSV;
for i= 1:lin
for j = 1:col
d(i,j) = ((i-lo)^2 + (j-co)^2)^0.5;
if d(i,j,:) > 90
Circle_Image_bilibinWar_HSV(i,j,:) = 0;
end
end
end
Circle_Image_bilibinWar_RGB = hsv2rgb(Circle_Image_bilibinWar_HSV);
figure(2);imshow(Circle_Image_bilibinWar_RGB); % i have isolated the circle
%% here im going to isolate the helmets inside the circle
figure(3); imshow(Circle_Image_bilibinWar_HSV); % as u can see here i will try to isolate the helmets from the rest
New_Circle_Image_bilibinWar_HSV = Circle_Image_bilibinWar_HSV % assinging a new image so as i can work on it seperatly;
valueH = New_Circle_Image_bilibinWar_HSV(lo,co,1);
for i=1:lin
for j=1:col
if (New_Circle_Image_bilibinWar_HSV(i,j,1) > valueH + 0.19)
New_Circle_Image_bilibinWar_HSV(i,j,:) = 0;
end
end
end
Circle_Image_bilibinWar_HSV = Circle_Image_bilibinWar_HSV - New_Circle_Image_bilibinWar_HSV;
NEW_Image_bilibinWar_RGB = hsv2rgb(Circle_Image_bilibinWar_HSV);
figure(4); subplot(1,2,1);imshow(ImagebilibinWar);
subplot(1,2,2);imshow(NEW_Image_bilibinWar_RGB);
So can some one help me with it? i don't know if its good and if not how can i make it good ?
I don't think there is anything wrong with your code. The answer is that segmenting using euclidean distance in colors simply does not work for RGB or HSV spaces. The entire purpose of the L*a*b color space was indeed this, creating a color space where similar colors would have the little euclidean distance.
Here a less cluttered version of it:
clear all; close all;
ImagebilibinWar = imread("https://i.stack.imgur.com/wnBDu.jpg");
[lin, col, pla] = size(ImagebilibinWar);
figure;imshow(ImagebilibinWar);
ImagebilibinWarHSV= rgb2hsv(ImagebilibinWar);
[lo,co] = ginput(1);
lo = round(lo);
co = round(co);
RIOH = ImagebilibinWarHSV(lo,co,1);
RIOS = ImagebilibinWarHSV(lo,co,2);
RIOV = ImagebilibinWarHSV(lo,co,3);
%reference value that i have to choose
for a=0:0.05:1
d = ((ImagebilibinWarHSV(:,:,1)-RIOH).^2 + (ImagebilibinWarHSV(:,:,2)-RIOS).^2+(ImagebilibinWarHSV(:,:,3)-RIOV).^2).^0.5;
ImagebilibinWarRGB = hsv2rgb(ImagebilibinWarHSV.*double(d<a));
figure;imshow(ImagebilibinWarRGB);
end

how do i mask labeled object based on some specified threshold value for each objects area,majoraxis and minoraxis?

i am currently using bwconnomp to label each connected object and regionpropsto find area, majoraxis, minoraxis of each labeled object respectively. i am also displaying each labeled object its area,majoraxis and minoraxis. now i want to set some threshold for area,majoraxis and minoraxis and if the value of area,majoraxis and minoraxis is above specified threshold then that object has to be masked.how this can be done??
here is my code
clc
clear all
close all
Index = 1;
scrsz = get(0,'ScreenSize');
%read an image
while Index ~= 0
% Open a dialog and select an image file
[FileName,FilePath,Index] = uigetfile('*.png', 'Open Imagefile ');
if Index == 0
disp('Procedure Done')
break;
end
inimage = imread([num2str(FilePath) FileName]);
D=inimage;
A=inimage;
subplot(2,3,1);
imshow(inimage);
title('original image');
%labeling algorithm
B=im2bw(inimage);
C=imfill(B,'holes');
label=bwlabel(C);
max(max(label))
CC = bwconncomp(B);
data = regionprops(CC,'all');
for j=1:max(max(label))
[row, col] = find(label==j);
len=max(row)-min(row)+2;
breadth=max(col)-min(col)+2;
target=uint8(zeros([len breadth] ));
sy=min(col)-1;
sx=min(row)-1;
for i=1:size(row,1)
x=row(i,1)-sx;
y=col(i,1)-sy;
target(x,y)=A(row(i,1),col(i,1));
end
mytitle=strcat('Object Number:' ,num2str(j),'area:', num2str(data(j).Area),'MajorAxis: ',num2str(data(j).MajorAxisLength),'MinorAxis: ',num2str(data(j).MinorAxisLength));
figure,imshow(target);title(mytitle);
a=size(target);
ax=a(1);
ay=a(2);
pos=[1,1,ay,ax];
rectangle('Position',pos,'EdgeColo','r')
end
next = input('next image? press Enter: ');
if next == 0
channelactivity = 0;
break
else
close all
disp('==================================')
pause(0.2)
continue
end
end
Here is a way to do it. The code is commented so easy to follow; the important line is the following:
AboveAreaIndices = find(vertcat(data.Area) > SomeValue)
In which you store the indices of the objects whose area is larger than SomeValue. In the example I color them red but you can do whatever you want with them or remove them altogether from the data structure.
You can also use logical operators to combine multiple conditions for example using the MinorAxis and MajorAxis properties. Note that I used AllArea as anew variable to store the concatenated areas to make things clearer, but you can keep them as vertcat(data.Area).
AboveIndices = find(vertcat(data.Area) > SomeValue & vertcat(data. MinorAxis) > SomeValue & Bla bla bla...);
Whole code:
clear
clc
close all
%// Read and clean up sample image
A = imread('rice.png');
A = im2bw(A,.5);
A = bwareaopen(A,50);
CC = bwconncomp(A);
%// Same as you.
data = regionprops(CC,'all');
%// Concatenate all the areas into an array.
AllArea = vertcat(data.Area);
%//========================================
%//==== Apply threshold on area here \\====
AboveAreaIndices = find(AllArea > 150);
%// If you wish to remove the entries from the data structure
% data(AllArea>150) = [];
%//========================================
%// Same for centroids...for display purposes
AllCentroids = vertcat(data.Centroid);
%// Display original and thresholded objects. Use the indices calculated
%// above to "mask" large areas if you want
imshow(A);
hold on
scatter(AllCentroids(:,1),AllCentroids(:,2),40,'b','filled')
scatter(AllCentroids(AboveAreaIndices,1),AllCentroids(AboveAreaIndices,2),40,'r','filled')
And sample output:

Matlab identify objects at boundary

I know basic commands in order to identify objects in a picture like:
level = graythresh(bw);
bw = im2bw(bw,level);
cc = bwconncomp(bw, 4);
cc.NumObjects;
graindata = regionprops(cc, 'basic');
perimeter = regionprops(cc, 'perimeter');
Those codes above is the code I am using.
In the picture attached, I can get the number to be 4. So the code identify that there is in total 4 objects.
However, this picture actually contains two objects. If we replicate this picture and move the replicate to the up, down, left and right, we can see that there is only two objects. But they are "separated" by the boundary.
It is not doable to change the way of making the image so the only way I can think of is to use some function or codes in matlab.
I will really appreciate it if someone can provide some matlab function to solve this problem.
All you need to do is loop over the border rows and columns and merge any regions that line up on opposite sides. The following code will produce an image with the regions labelled by number in the way you want.
cc=bwconncomp(bw);
[rows,cols] = size(reg);
% matrix of region labels
regions = uint8(zeros(rows,cols));
% label each pixel with an integer for its region number
for i = 1:length(cc.PixelIdxList)
region(cc.PixelIdxList{i}) = i;
end
% loop over rows, merge the regions if pixels line up
for i = 1:rows
left = region(i,1);
right = region(i,end);
if (left>0) && (right>0) && (left~=right)
region(region==right) = left;
end
end
% loop over columns, merge the regions if pixels line up
for j = 1:cols
top = region(1,j);
bottom = region(end,j);
if (top>0) && (bottom>0) && (top~=bottom)
region(region==bottom) = top;
end
end

Matlab GUI for array of spots

I need to create a GUI in Matlab. It requires me to identify the spots for two images, and calculate the distance between them.
I have obtained the code for finding and encircling a single spot. It is as follows:
function [meanx,meany] = centroid(pic)
[x,y,z] = size(pic);
if(z==1)
;
else
pic = rgb2gray(pic);
end
% N=2;
% image = interp2(double(pic),N,'spline');
image = sort(sort(pic,1),2);
image =reshape(image,1,numel(image));
i=0;
while(i<3)
if(image(end)==image(end-1))
image(end)=[];
else
image(end)=[];
i=i+1;
end
end
threshold = image(end);
pic2 =(pic>=threshold);
pic=(pic-threshold).*uint8(pic2);
% % image=(pic-threshold+1).*uint8(image); %minus threshold
[rows,cols] = size(pic);
x = ones(rows,1)*[1:cols];
y = [1:rows]'*ones(1,cols);
area = sum(sum(pic));
if area ~= 0
meanx = sum(sum(double(pic).*x))/area;
meany = sum(sum(double(pic).*y))/area;
else
meanx = cols/2;
meany = rows/2;
end
However, I need it to work for multiple spots as shown below :
http://imgur.com/oEe0mRV,UAnbH5y#0
http://imgur.com/oEe0mRV,UAnbH5y#1
So far, I have come up with this, but it only circles separate spots and not all together.
PLEASE HELP - I need to encircle at least 10X10 spots and store their values, and do this for two images as shown above, and find the distance between them!
img1 = imread('r0.bmp');
centroidmat=zeros(10,10,2);
for numx=1:2
for numy=1:2
single_spot=img1((numx*220+780):((numx+1)*220+780),(numy*220+1272):((numy+1)*220+1272),:);
figure
imshow(single_spot);
figure
[cx,cy] = centroid(single_spot);
centroidmat(numx,numy,1)=cx;
centroidmat(numx,numy,2)=cy;
imshow(single_spot);
hold on;
plot(cx,cy,'og')
end
end
Please HELP GUYS! Any help is appreciated!
Would this work ? -
centroidmat=zeros(10,10,2);
for numx=1:2
for numy=1:2
single_spot=img1((numx*220+780):((numx+1)*220+780),(numy*220+1272):((numy+1)*220+1272),:);
[cx,cy] = centroid(single_spot);
centroidmat(numx,numy,1)=cx;
centroidmat(numx,numy,2)=cy;
figure,
imshow(single_spot);
hold on;
plot(cx,cy,'og')
end
end
I have only removed the redundant figure and imshow(single_spot); at the start of the loop, as they appear again later on inside the same loop.

Rolling window for averaging using MATLAB

I have the following code, pasted below. I would like to change it to only average the 10 most recently filtered images and not the entire group of filtered images. The line I think I need to change is: Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;, but how do I do it?
j=1;
K = 1:3600;
window = zeros(1,10);
Yout = zeros(10,column,row);
figure;
y = 0; %# Preallocate memory for output
%Load one image
for i = 1:length(K)
disp(i)
str = int2str(i);
str1 = strcat(str,'.mat');
load(str1);
D{i}(:,:) = A(:,:);
%Go through the columns and rows
for p = 1:column
for q = 1:row
if(mean2(D{i}(p,q))==0)
x = 0;
else
if(i == 1)
meanvalue = mean2(D{i}(p,q));
end
%Calculate the temporal mean value based on previous ones.
meanvalue = (meanvalue+D{i}(p,q))/2;
x = double(D{i}(p,q)/meanvalue);
end
%Filtering for 10 bands, based on the previous state
for k = 1:10
[y, ZState{k}] = filter(bCoeff{k},aCoeff{k},x,ZState{k});
Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;
end
end
end
% for k = 2:10
% subplot(5,2,k)
% subimage(Yout(k)*5000, [0 100]);
% colormap jet
% end
% pause(0.01);
end
disp('Done Loading...')
The best way to do this (in my opinion) would be to use a circular-buffer to store your images. In a circular-, or ring-buffer, the oldest data element in the array is overwritten by the newest element pushed in to the array. The basics of making such a structure are described in the short Mathworks video Implementing a simple circular buffer.
For each iteration of you main loop that deals with a single image, just load a new image into the circular-buffer and then use MATLAB's built in mean function to take the average efficiently.
If you need to apply a window function to the data, then make a temporary copy of the frames multiplied by the window function and take the average of the copy at each iteration of the loop.
The line
Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;
calculates a kind of Moving Average for each of the 10 bands over all your images.
This line calculates a moving average of meanvalue over your images:
meanvalue=(meanvalue+D{i}(p,q))/2;
For both you will want to add a buffer structure that keeps only the last 10 images.
To simplify it, you can also just keep all in memory. Here is an example for Yout:
Change this line: (Add one dimension)
Yout = zeros(3600,10,column,row);
And change this:
for q = 1:row
[...]
%filtering for 10 bands, based on the previous state
for k = 1:10
[y, ZState{k}] = filter(bCoeff{k},aCoeff{k},x,ZState{k});
Yout(i,k,p,q) = y.^2;
end
YoutAvg = zeros(10,column,row);
start = max(0, i-10+1);
for avgImg = start:i
YoutAvg(k,p,q) = (YoutAvg(k,p,q) + Yout(avgImg,k,p,q))/2;
end
end
Then to display use
subimage(Yout(k)*5000, [0 100]);
You would do sth. similar for meanvalue