Sack11
Sack11

Reputation: 173

How to create a mask or detect image section based on the intensity value?

I have a matrix named figmat from which I obtain the following pcolor plot (Matlab-Version R 2016b).

Basically I only want to extract the bottom red high intensity line from this plot.

I thought of doing it in some way of extracting the maximum values from the matrix and creating some sort of mask on the main matrix. But I'm not understanding a possible way to achieve this. Can it be accomplished with the help of any edge/image detection algorithms?

I was trying something like this with the following code to create a mask

A=max(figmat);
figmat(figmat~=A)=0;
imagesc(figmat);

But this gives only the boundary of maximum values. I also need the entire red color band.

pcolor plot

Upvotes: 1

Views: 195

Answers (2)

EBH
EBH

Reputation: 10450

By assuming:

  1. There is only one band to extract.
  2. It always has the maximum values.
  3. It is linear.

I can adopt my previous answer to this case as well, with few minor changes:

First, we get the distribution of the values in the matrix and look for a population in the top values, that can be distinguished from the smaller values. This is done by finding the maximum value x(i) on the histogram that:

  1. Is a local maximum (its bin is higher than that of x(i+1) and x(i-1))
  2. Has more values above it than within it (the sum of the height of bins x(i+1) to x(end) < the height of bin x):

This is how it is done:

[h,x] = histcounts(figmat); % get the distribution of intesities
d = diff(fliplr(h)); % The diffrence in bin height from large x to small x
band_min_ind = find(cumsum(d)>size(figmat,2) & d<0, 1); % 1st bin that fit the conditions
flp_val = fliplr(x); % the value of x from large to small
band_min = flp_val(band_min_ind); % the value of x that fit the conditions

Now we continue as before. Mask all the unwanted values, interpolate the linear line:

mA = figmat>band_min; % mask all values below the top value mode
[y1,x1] = find(mA,1); % find the first nonzero row 
[y2,x2] = find(mA,1,'last'); % find the last nonzero row
m = (y1-y2)/(x1-x2); % the line slope
n = y1-m*x1; % the intercept
f_line = @(x) m.*x+n; % the line function

And if we plot it we can see the red line where the band for detection was:

line a

Next, we can make this line thicker for a better representation of this line:

thick = max(sum(mA)); % mode thickness of the line
tmp = (1:thick)-ceil(thick/2); % helper vector for expanding
rows = bsxfun(@plus,tmp.',floor(f_line(1:size(A,2)))); % all the rows for each column
rows(rows<1) = 1; % make sure to not get out of range
rows(rows>size(A,1)) = size(A,1); % make sure to not get out of range
inds = sub2ind(size(A),rows,repmat(1:size(A,2),thick,1)); % convert to linear indecies
mA(inds) = true; % add the interpolation to the mask
result = figmat.*mA; % apply the mask on figmat

Finally, we can plot that result after masking, excluding the unwanted areas:

imagesc(result(any(result,2),:))

line b

Upvotes: 2

Loamsiada
Loamsiada

Reputation: 454

Okay, I assume that the red line is linear and its values can uniquely be separated from the rest of the picture. Let's generate some test data...

[x,y] = meshgrid(-5:.2:5, -5:.2:5);
n = size(x,1)*size(x,2);
z = -0.2*(y-(0.2*x+1)).^2 + 5 + randn(size(x))*0.1;
figure
surf(x,y,z);

This script generates a surface function. Its set of maximum values (x,y) can be described by a linear function y = 0.2*x+1. I added a bit of noise to it to make it a bit more realistic.

Test Data

We now select all points where z is smaller than, let's say, 95 % of the maximum value. Therefore find can be used. Later, we want to use one-dimensional data, so we reshape everything.

thresh = min(min(z)) + (max(max(z))-min(min(z)))*0.95;
mask = reshape(z > thresh,1,n);
idx = find(mask>0);
xvec = reshape(x,1,n);
yvec = reshape(y,1,n);

xvec and yvec now contain the coordinates of all values > thresh.

Filtered points

The last step is to do some linear polynomial over all points.

pp = polyfit(xvec(idx),yvec(idx),1)

pp =

    0.1946    1.0134

Obviously these are roughly the coefficients of y = 0.2*x+1 as it should be.

Fitted line

I do not know, if this also works with your data, since I made some assumptions. The threshold level must be chosen carefully. Maybe some preprocessing must be done to dynamically detect this level if you really want to process your images automatically. There might also be a simpler way to do it... but for me this one was straight forward without the need of any toolboxes.

Upvotes: 3

Related Questions