How do I determine how do these black dots vary by location in an equal spaced magnetic field.

I circled some of the black dots I want to count in the red circle. I want to segment each area equally across the image in the same crescent shape as I have drawn but equally spaced apart. But I only want to segment the areas that are filled with black dots and not segment the areas with the magnetic field (where the magnet is). This is because since the magnet expels all of the black dots away to other regions if I segment and count the space where the magnet is it wouldn't give an accurate account of the number of cells when I compare it to other cells where the magnet is a different location.
How can I detect where to start segmenting such that I only segment the areas with the black dots and around the areas without them?
I attempted to make a for loop code that starts segmenting by columns and substract by 1 before it starts counting but sometimes the magnetic field varies in location so I can't always do that, so is there a way to detect that this area shouldnt counted and count around this area then uniformly segment the remainder of the image like in the crescent shape i've drawn? I attached a picture of a pond that gives an idea of what I am attempting to try. the rock is the area I don't want to count, the cirlce around it would be the starting counting and the subsequent waves produced would be the other counts until the last column in the image. Please advise for an old R user who now loves MATLAB. Thanks.

 Risposta accettata

Do you have some equation for where the lines are? Like ar they perfect circles centered at the magnet or have some other formula? If you do then you can make a mask for each zone and compute the mean gray level in that zone. Can you attach the original, un-annotated image?
In the meantime, I'm attaching a few demos.

14 Commenti

I attached two example images that I am working on.
The lines would be perfect circles centered at the magnet (the dark area in the screenshots I attached).
I think your average_radial_profile_2 would work for me I will have a look at this. Please move to answer and I will accept this, thank you.
I do have a question, is the average gray level the same as counting the spots that are dark and graphing them by their location in the image?
Not quite the same but it should correlate. Some of the spots would be in multiple zones, and that's if we could even segment them properly. It looks like many blobs are connected into just one giant blob. So finding the "number of blobs" as a function of radius or zone number is not a well defined thing. Since the gray level correlates with the number of blobs, you're best off just measuring what you CAN measure and that is the gray level as a function of distance. You can convert to gray scale and get the radius, or leave it as color and do it for each individual color channel separately. Attach you code if you need any help.
Thanks for this, ol'chap like me can still learn new things.
Check out my attached m file.
I intended to make the 12 strips that are measuring the gray level 12 perfect semi circles starting from the center that I choose. But it's either giving me an error or not doing what I intended, what do you suggest? Let me know if i should move to a seperate question.
@Neo sorry that's not working, but thanks for trying. This is what I was thinking of.
% Demo by Image Analyst
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables. Or clearvars if you want.
workspace; % Make sure the workspace panel is showing.
format long g;
format compact;
fontSize = 18;
markerSize = 40;
%--------------------------------------------------------------------------------------------------------
% READ IN TEST IMAGE
folder = [];
baseFileName = 'Screenshot 2023-02-15 at 6.23.03 PM.png';
fullFileName = fullfile(folder, baseFileName);
% Check if file exists.
if ~exist(fullFileName, 'file')
% The file doesn't exist -- didn't find it there in that folder.
% Check the entire search path (other folders) for the file by stripping off the folder.
fullFileNameOnSearchPath = baseFileName; % No path this time.
if ~exist(fullFileNameOnSearchPath, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist in the search path folders.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
grayImage = imread(fullFileName);
% Get the dimensions of the image.
% numberOfColorChannels should be = 1 for a gray scale image, and 3 for an RGB color image.
[rows, columns, numberOfColorChannels] = size(grayImage)
if numberOfColorChannels > 1
% It's not really gray scale like we expected - it's color.
fprintf('It is not really gray scale like we expected - it is color\n');
% Extract the blue channel.
grayImage = rgb2gray(grayImage);
end
%--------------------------------------------------------------------------------------------------------
% Display the image.
subplot(2, 2, 1);
imshow(grayImage, []);
impixelinfo;
axis('on', 'image');
title('Original Gray Scale Image', 'FontSize', fontSize, 'Interpreter', 'None');
% Update the dimensions of the image.
% numberOfColorChannels should be = 1 for a gray scale image, and 3 for an RGB color image.
[rows, columns, numberOfColorChannels] = size(grayImage)
% Maximize window.
g = gcf;
g.WindowState = 'maximized';
drawnow;
%--------------------------------------------------------------------------------------------------------
% Display histogram
subplot(2, 2, 2);
histogram(grayImage);
grid on;
title('Histogram of Image', 'FontSize', fontSize, 'Interpreter', 'None');
%--------------------------------------------------------------------------------------------------------
% Threshold to create mask
lowThreshold = 0;
highThreshold = 40;
% Interactively and visually set a threshold on a gray scale image.
% https://www.mathworks.com/matlabcentral/fileexchange/29372-thresholding-an-image?s_tid=srchtitle
% [lowThreshold, highThreshold] = threshold(lowThreshold, highThreshold, grayImage)
mask = grayImage >= lowThreshold & grayImage <= highThreshold;
xline(highThreshold, 'Color', 'r', 'LineWidth', 2)
subplot(2, 2, 3);
% Take only the largest blob.
mask = bwareafilt(mask, 1);
imshow(mask);
impixelinfo;
axis('on', 'image');
title('Mask Image with Centroid Marked', 'FontSize', fontSize, 'Interpreter', 'None');
%--------------------------------------------------------------------------------------------------------
% Find the centroid of the blob.
props = regionprops(mask, 'Centroid');
xCenter = props.Centroid(1);
yCenter = props.Centroid(2);
hold on;
% Plot the centroid in red for this method.
plot(xCenter, yCenter, 'r+', 'LineWidth', 2, 'MarkerSize', 50);
% An alternate way if the blob is partially off the edge of the image is to
% Get the coordinates with bwboundaries and then exclude edge pixels then to
% Fit the coordinates to a circle.
b = bwboundaries(mask);
b = b{1};
x = b(:, 2);
y = b(:, 1);
% Exclude edge pixels
edgeIndexes = (x == 1) | (x == columns) | (y == 1) | (y == rows);
x(edgeIndexes) = [];
y(edgeIndexes) = [];
plot(x, y, 'r-', 'LineWidth', 2);
% Fit those coordinates to a circle
[xCenter, yCenter, radius, a] = circlefit(x, y);
% Plot the centroid in Cyan for this method.
plot(xCenter, yCenter, 'c+', 'LineWidth', 2, 'MarkerSize', 50);
% Allocate an array for the profile
maxDistance = ceil(sqrt(columns ^ 2 + rows ^ 2))
profileSums = zeros(1, maxDistance);
profileCounts = zeros(1, maxDistance);
% Scan the original image getting distance from centroid.
% Then add those values to the profile.
for column = 1 : columns
for row = 1 : rows
thisDistance = round(sqrt((column - xCenter) ^2 + (row - yCenter) ^ 2));
if thisDistance <= 0
continue;
end
if thisDistance >= 838
continue;
end
profileSums(thisDistance) = profileSums(thisDistance) + double(grayImage(row, column));
profileCounts(thisDistance) = profileCounts(thisDistance) + 1;
end
end
% Find last index so we don't consider unused ones.
lastIndex = find(profileCounts > 0, 1, 'last')
profileCounts = profileCounts(1 : lastIndex);
profileSums = profileSums(1 : lastIndex);
% Divide the sums by the counts at each distance to get the average profile
averageRadialProfile = profileSums ./ profileCounts;
subplot(2, 2, 4);
plot(1:length(averageRadialProfile), averageRadialProfile, 'b-', 'LineWidth', 3);
grid on;
title('Average Radial Profile', 'FontSize', fontSize);
xlabel('Distance from nearest white region', 'FontSize', fontSize);
ylabel('Gray Level', 'FontSize', fontSize);
%===========================================================================================================================================
function [xCenter, yCenter, radius, a] = circlefit(x, y)
% circlefit(): Fits a circle through a set of points in the x - y plane.
% USAGE :
% [xCenter, yCenter, radius, a] = circlefit(X, Y)
% The output is the center point (xCenter, yCenter) and the radius of the fitted circle.
% "a" is an optional output vector describing the coefficients in the circle's equation:
% x ^ 2 + y ^ 2 + a(1) * x + a(2) * y + a(3) = 0
% by Bucher Izhak 25 - Oct - 1991
numPoints = numel(x);
xx = x .* x;
yy = y .* y;
xy = x .* y;
A = [sum(x), sum(y), numPoints;
sum(xy), sum(yy), sum(y);
sum(xx), sum(xy), sum(x)];
B = [-sum(xx + yy) ;
-sum(xx .* y + yy .* y);
-sum(xx .* x + xy .* y)];
a = A \ B;
xCenter = -.5 * a(1);
yCenter = -.5 * a(2);
radius = sqrt((a(1) ^ 2 + a(2) ^ 2) / 4 - a(3));
end
can I put the average radial profile in a bar graph instead of as plotted in above?
No doubt it looks good Analyst!! However, can you see in my code it is attempting to get the radial profile over 12 locations? I don't see that yours does that. i know mine is not working, how can I fix that?
@Neo your maxDistance was not computed correctly. Plus your masks are vertical rectangles intead of donut shapes.
You could use a bar chart instead of a line plot if you want.
If you wanted to quantize mine into certain "zones" you could of course take the mean of the profile between a specified pair of radii, like between 100 and 200, 200 and 300, 300 and 400, etc.
How was it not computed correctly? my image was also a square. Can't i just compute it the same on my image?
i do not know how to make my mask shaped like a donut, I tried the linspace function but it can't take a matrix as one of its inputs. How could I make my mask a donut shape?
Instead of quantizing, I tried to stuff the radial profile into an array and plot inta the graph. But it keeps on popping up blank. What would you write instead?
There was an error in your code because you were trying to add in a value at a certain radius and you didn't have any values at that radius because your maxDistance was too small and so your arrays were preallocated with not enough elements.
See the FAQ for how to create a circular mask.
Doing it twice will give you a donut.
prior to the mask what prefiltering analysis were applied? Usually, there is some filtering, removal of the background and correction for contrast with binarization is there a way to include this if not already included? Thanks!!
@Chanille I didn't do any prefiltering. I just took the intensity of the original image. If you have a nonuniform background then you could do a background correction. You wouldn't want to do any contrast or intensity changing since you want (I believe) the original gray levels. I don't see how binarization would help either. You're not measuring blob shapes or other attributes.
I understand. Thanks for the clarification.
Sincerely,
C.
@Image Analyst Check this out. But I still get an error and I have done everything suggested (or maybe I need to restart my computer not sure at this point). What do you think?

Accedi per commentare.

Più risposte (2)

Neo
Neo il 15 Feb 2023
Modificato: Neo il 15 Feb 2023
This looks like something that I recently learned to do in Matlab with one of @Image Analyst analyst demo. Look at my question with his response (can’t remember which but it was recent) and you’ll see you could use that code and change the shape of the segments to get what you want.

1 Commento

Try this:
startingColumns = round(linspace(1, columns+1, 10))
endingColumns = startingColumns(2:end) - 1
in your for loop. This would segment 9 columns equally across your image BUT IN STRIPS.
Something like this could give you the semi circle (half moon shape segmentation) that you want:
theta = linspace( pi/2, -pi/2, 100); So maybe you could add that into the starting columns. I am not sure how to detect the non black dot area though. I will have to think about that.

Accedi per commentare.

Categorie

Richiesto:

il 15 Feb 2023

Commentato:

Neo
il 22 Feb 2023

Community Treasure Hunt

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

Start Hunting!

Translated by