Bounding data in matlab - matlab

Demo data
Data set can be found here :
Dataset
Each point (x;y) indicated the value (y) of the xth sample measured
I am trying to bound a data region which has the most data points just like in the figure , by using 2 line y=a and y=b
How can i approach this solution

Let's say your minimum and maximum for y values are respectively 960 and 972:
y(y < 960) = 960;
y(y > 972) = 972;
Alternatively, you can remove those outliers instead of bounding them:
y_idx = find((y < 960) | (y > 972));
x(y_idx) = [];
y(y_idx) = [];

Related

Counting the sum coins, using Matlab Image Processing Toolbox

I am trying to develop a program which would find a circular objects (coins) and measure their size, based on which I will assign values. I need to take into account the position form which a particular photo is taken, e.g. angle. Since some coins will appear larger than others from different angles, the value assignment will be defective, hence I am trying to solve it with projective transformation (projecting a tilted image) and so far, I am failing to project oblique photos in a way that produces more or less equal measurements of the diameters. My code is below:
I = imread('coins2.jpg');
imshow(I);
M = ginput(4);
X = ginput(4);
P = maketform('projective', M, X);
I = imtransform(I, P, 'nearest');
G = rgb2gray(I);
K = medfilt2(G);
[centers,radii] = imfindcircles(K,[90 140],'ObjectPolarity','dark','Sensitivity',0.95);
h = viscircles(centers, radii);
arr = [];
for r = 1:length(radii)
if (radii(r)) >= 105
arr(r) = 1;
elseif (radii(r)) < max(radii) && radii(r) > 100
arr(r) = 0.2;
elseif(radii(r)) < 100 && (radii(r)) > 95
arr(r) = 0.05;
elseif (radii(r)) < 95 && (radii(r)) > 90
arr(r) = 0.1;
end
end
result = sum(arr);
If you have the option to detect corners in the pictures (for example, coins on a rectangular support), you could use this Matlab file:
https://kr.mathworks.com/matlabcentral/fileexchange/35531-perspective-control-correction to detect 4 corners manually or automatically to correct the perspective, and you would then be able to use imfindcircles as usual.

Trying to apply a color map to a bar graph in MATLAB

I have a collection of data that I am trying to graph as a histogram. Additionally, I would like to color the individual bars as a function of the x axis location. CData, described here seems to do what I want but I can't get it to work.
Here is my code:
h = bar(new_edge,N,'hist','FaceColor','flat');
hold on
for n = 1:length(N)
if (x - x/1.09) - (x-1) > 0
probability(n) = 1 - ((x-x/1.09) - (x-1))/((x - 1/1.09)+(x/0.91 - x));
else
probability(n) = 1;
end
color_num = 30;
cm = jet(color_num);
min = 0.5450;
max = 1;
color_map_index = floor(1 + (probability(n) - min)/(max-min)*(color_num-1));
rbg = cm(color_map_index,:);
h.CData(n,:) = rbg;
end
Similar to the MATLAB example, I first create my bar graph. Next, I want to loop through and prescribe the color for each bar based on a calculation. I do this by creating a colormap with # of bins and a min/max, getting a color index, then finally retrieving the rbg value. I get the following error when I try to apply the color:
Subscript indices must either be real positive integers or logicals.
h.CData(n,:) = rbg;
If I dive into the h object, MATLAB tells me that CData has a size of (4x65). What's going on here? Both new_edge and N are 1x65 vectors.
Are you certain that the error you're getting ("Subscript indices must either be real positive integers or logicals") is coming from the following line?:
h.CData(n,:) = rbg;
Since we know n is a positive integer greater than or equal to one, the indexing here shouldn't have a problem. It's more likely that your error is coming from the line above it (i.e. the value for color_map_index is less than 1). I would double-check how you are computing color_map_index.
You could also try using function notation (i.e. get and set) instead of dot notation to update the property:
cData = get(h, 'CData');
cData(n, :) = rbg;
set(h, 'CData', cData);
Incidentally, you should also not be giving your variables the same name as existing functions, like you are doing here:
...
min = 0.5450;
max = 1;
...
This shadows the built-in min and max functions, which can also lead to the same error message under other conditions. Definitely rename those.
If you are still having trouble after trying those fixes, you could try setting the color using indexed color mapping instead, as illustrated in one of my other answers (near the bottom). As a simple example, the following plots 20 bars of 30 different possible values, then colors them based on their height:
color_num = 30;
N = randi(color_num, 1, 20);
hBar = bar(N, 'hist');
colormap(parula(color_num));
set(hBar, 'CData', N, 'CDataMapping', 'direct');
And the plot:
This could be a problem with your Matlab version.
When I test CData with bar on 2017b, this works:
openExample('graphics/ControlIndividualBarColorsExample')
When I try it on 2017a, it doesn't run.
Does the example work?
Given that this is a version control problem, there's really not a clean solution. In case anyone else comes along with a similar question and has the same version, here's a workaround that worked for me in 2017a.
Rather than creating a bar chart, you can simply draw rectangles. It's messy, but it does produce the desired result.
[N,edges] = histcounts(AB(AB<2));
probability = zeros(1,length(N));
new_edge = zeros(1,length(N));
for m = 1:length(N)
new_edge(m) = (edges(m) + edges(m+1))/2;
end
figure
hold on
for n = 1:length(N)
x = new_edge(n);
if ((x - x/1.09) - (x-1)) > 0
probability(n) = 1 - ((x-x/1.09) - (x-1))./((x - x/1.09)+(x/0.91 - x));
else
probability(n) = 1;
end
color_num = 100;
cm = jet(color_num);
min = 0;
max = 1;
color_map_index(n) = floor(1 + (probability(n) - min)/(max-min)*(color_num-1));
rbg = cm(color_map_index(n),:);
rectangle('Position',[edges(n),0,(edges(n+1)-edges(n)),N(n)],'Edgecolor','k','FaceColor',rbg)
end
set(gcf,'color','w');
blah = colorbar;

Undertanding a low pass filter code in image processing

The aim of this piece of Matlab code is to smooth a horizontal histogram by applying a low pass filter.
First horizontal and vertical histograms representing the sum of differences of gray values between neighboring pixels of an image, column-wise and row-wise were used. The horizontal histogram is named horz1 so horz1(i)=sum where 'i' is the column number and 'sum' is the sum of differences. Then, a low pass filter was applied. I don't understand the 'applying low pass filter' part.I don't know much about Image processing. If anyone could help me understand I would really appreciate it.
%% horizontal histogram
disp('Processing Edges Horizontally...');
max_horz = 0;
maximum = 0;
for i = 2:cols
sum = 0;
for j = 2:rows
if(I(j, i) > I(j-1, i))
difference = uint32(I(j, i) - I(j-1, i));
else
difference = uint32(I(j-1, i) - I(j, i));
end
if(difference > 20)
sum = sum + difference;
end
end
horz1(i) = sum;
%%applying low pass filter
sum = 0;
horz = horz1;
for i = 21:(cols-21)
sum = 0;
for j = (i-20):(i+20)
sum = sum + horz1(j);
end
horz(i) = sum / 41;
end
end
It simply finds the average over a 41 element window. Low-pass filter retains low-frequency components and suppresses high frequency components. Averaging is an example of a low-pass filter.
The explanation of the two for loops at the end when calculating the filtered output is as follows. It starts at i = 21 because it needs to collect 20 values before and after without going out of bounds including the middle: 20 + 20 + 1 = 41. When i = 21, then j = 1 to 41, then when i = 22, j = 2 to 42 etc. up to cols - 21, which is thus j = cols - 41 up to cols - 1. Technically we should end at i = cols - 20 though, but that's ok if you want to miss calculating the final valid output of the signal.
The loop controlled by i determines what the centre of the window is and the loop controlled by j collects the right samples dictated by i. You'll need to think about it especially if you aren't used to this, but eventually it will make sense.

How can we evaluate a Gaussian in an intensity of an Image? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
You can refer to section 4.3.1 in this article if you want.
If pI is any pixel/intensity on this image, and dS is the (rho, theta) of that line in the Hough Space, what is the meaning of the following statement?
Is the following a correct implementation?
function val = gaussC(pI, sigma, dS)
x = pI(1);
y = pI(2);
rho = dS(1);
theta = dS(2);
exponent = ((x-rho).^2 + (y-theta).^2)./(2*sigma);
val = (exp(-exponent));
end
EDIT:
My second proposal,
I = gray_imread('Scratch1.png');
dimesnsion = 5;
sigma = 1;
pI = [22, 114];
dS = [-108, -80];
J = get_matrix_from_image(I, pI, dimension);
var = normpdf(J(:), dS(2), sigma);
get_matrix_from_image.m
function mat = get_matrix_from_image(input_image, ctr_point, dimension)
[height, width] = size(input_image);
col_count = width;
row_count = height;
xxx = col_count;
yyy = row_count;
if(ctr_point(1) < 1 && ctr_point(2) < 1)
mat = zeros(dimension, dimension);
else
x = ctr_point(1);
y = ctr_point(2);
start_x = x - floor(dimension/2);
end_x = start_x + dimension - 1;
start_y = y - floor(dimension/2);
end_y = start_y + dimension - 1;
if(start_x > xxx || end_x>xxx || start_y > yyy || end_y>yyy || ...
start_x < 1 || end_x<1 || start_y <1 || end_y<1)
mat = zeros(dimension, dimension);
else
mat = input_image(start_x:end_x, start_y:end_y);
end
end
end
Not quite. Basically you are manually coding formula (9) from here. Then:
...
exponent = ((x-rho).^2 + (y-theta).^2)./(2*sigma^2); % sigma is also squared
val = exp(-exponent); % superfluous bracket removed
val = val./(2*pi*sigma^2); % you also forgot the denominator part
end
Of course you could write the whole thing a bit more efficient. But unless you actually want to use this formula on a lot of data I would keep it like this (it's very readable).
If you value performance, just use the built in function:
val = normpdf(pI,dS,sigma)
For new readers of this question: The OP reopened this questions after editing it heavily, completely changing the nature of the question. Therefore this answer now seems a bit off.
Your code is the incorrect implementation of the PDF of the normal distribution. The PDF of the normal distribution is:
IMO If pI is defined by x and y, i.e. pI(x,y), and dS is defined by rho and theta, i.e. dS(rho,theta) then you cannot simply subtract rho from x and theta from y . You have to convert one of them to the other. In my code, I have converted dS(rho,theta) to dS(x,y) and then used it as μ in the formula of PDF.
Furthermore, I think pI would be a matrix of 5 rows and 2 columns (5 pixels with x and y values) saying this on the basis of the following figure which is Figure 6 of the linked research:
Now coming to the statement,
g(pi , ds) is a Gaussian function, evaluated
in pi, with a peak in correspondence with the detected
scratch direction ds and a constant standard deviation.
IMO The author/s of the paper suggest/s to take 5 pixels, calculate PDF and find where the peak is.
Based on my understanding, its implementation should be:
function val = gaussC(pI,dS,sigma)
x = pI(:,1); %x values of all pixels
y = pI(:,2); %y values of all pixels
rho = dS(1);
theta = dS(2);
%Converting polar coordinates to rectangular coordinates to get mean value
%in x and y direction
exponent = [(x-rho*cos(theta)).^2 + (y-rho*sin(theta)).^2]./(2*sigma^2);
val = exp(-exponent)./(sigma*sqrt(2*pi));
end
After calculating the PDF, find the peak value.

Using MATLAB to calculate offset between successive images

I'm taking images using a tunneling microscope. However, the scope is drifting between successive images. I'm trying to use MatLab to calculate the offset between images. The code below calculates in seconds for small images (e.g. 64x64 pixels), but takes >2 hrs to handle the 512x512 pixel images I'm dealing with. Do you have any suggestions for speeding up this code? Or do you know of better ways to track images in MatLab? Thanks for your help!
%Test templates
template = .5*ones(32);
template(25:32,:) = 0;
template(:,25:64) = 0;
data_A = template;
close all
imshow(data_A);
template(9:32,41:64) = .5;
template(:,1:24) = 0;
data_B = template;
figure, imshow(data_B);
tic
[m n] = size(data_B);
z = [];
% Loop over all possible displacements
for x = -n:n
for y = -m:m
paddata_B = data_B;
ax = abs(x);
zerocols = zeros(m,ax);
if x > 0
paddata_B(:,1:ax) = [];
paddata_B = [paddata_B zerocols];
else
paddata_B(:,(n-ax+1):n) = [];
paddata_B = [zerocols paddata_B];
end
ay = abs(y);
zerorows = zeros(ay,n);
if y < 0
paddata_B(1:ay,:) = [];
paddata_B = vertcat(paddata_B, zerorows);
else
paddata_B((m-ay+1):m,:) = [];
paddata_B = vertcat(zerorows, paddata_B);
end
% Full matrix sum after array multiplication
C = paddata_B.*data_A;
matsum = sum(sum(C));
% Populate array of matrix sums for each displacement
z(x+n+1, y+m+1) = matsum;
end
end
toc
% Plot matrix sums
figure, surf(z), shading flat
% Find maximum value of z matrix
[max_z, imax] = max(abs(z(:)));
[xpeak, ypeak] = ind2sub(size(z),imax(1))
% Calculate displacement in pixels
corr_offset = [(xpeak-n-1) (ypeak-m-1)];
xoffset = corr_offset(1)
yoffset = corr_offset(2)
What you're calculating is known as the cross-correlation of the two images. You can calculate the cross-correlation of all offsets at once using Discrete Fourier Transforms (DFT or FFT). So try something like
z = ifft2( fft2(dataA) .* fft2(dataB).' );
If you pad with zeros in the Fourier domain, you can even use this sort of math to get offsets in fractions of a pixel, and apply offsets of fractions of a pixel to an image.
A typical approach to this kind of problem is to use the fact that it works quickly for small images to your advantage. When you have large images, decimate them to make small images. Register the small images quickly and use the computed offset as your initial value for the next iteration. In the next iteration, you don't decimate the images as much, but you're starting with a good initial estimate of the offset so you can constrain your search for solutions to a small neighborhood near your initial estimate.
Although not written with tunneling microscopes in mind, a review paper that may be of some assistance is: "Mutual Information-Based Registration of Medical Images: A Survey" by Pluim, Maintz, and Viergever published in IEEE Transactions on Medical Imaging, Vol. 22, No. 8, p. 986.
below link will help you find transformation between 2 images and correct/recover the distorted (in your case, image with offset)
http://in.mathworks.com/help/vision/ref/estimategeometrictransform.html
index_pairs = matchFeatures(featuresOriginal,featuresDistorted, 'unique', true);
matchedPtsOriginal = validPtsOriginal(index_pairs(:,1));
matchedPtsDistorted = validPtsDistorted(index_pairs(:,2));
[tform,inlierPtsDistorted,inlierPtsOriginal] = estimateGeometricTransform(matchedPtsDistorted,matchedPtsOriginal,'similarity');
figure; showMatchedFeatures(original,distorted,inlierPtsOriginal,inlierPtsDistorted);
The inlierPtsDistored, inlierPtsOriginal have attributes called locations.
These are nothing but matching locations of one image on another. I think from that point it is very easy to calculate offset.
The function below was my attempt to compute the cross-correlation of the two images manually. Something's not quite right though. Will look at it again this weekend if I have time. You can call the function with something like:
>> oldImage = rand(64);
>> newImage = circshift(oldImage, floor(64/2)*[1 1]);
>> offset = detectOffset(oldImage, newImage, 10)
offset =
32 -1
function offset = detectOffset(oldImage, newImage, margin)
if size(oldImage) ~= size(newImage)
offset = [];
error('Test images must be the same size.');
end
[imageHeight, imageWidth] = size(oldImage);
corr = zeros(2 * imageHeight - 1, 2 * imageWidth - 1);
for yIndex = [1:2*imageHeight-1; ...
imageHeight:-1:1 ones(1, imageHeight-1); ...
imageHeight*ones(1, imageHeight) imageHeight-1:-1:1];
oldImage = circshift(oldImage, [1 0]);
for xIndex = [1:2*imageWidth-1; ...
imageWidth:-1:1 ones(1, imageWidth-1); ...
imageWidth*ones(1, imageWidth) imageWidth-1:-1:1];
oldImage = circshift(oldImage, [0 1]);
numPoint = abs(yIndex(3) - yIndex(2) + 1) * abs(xIndex(3) - xIndex(2) + 1);
corr(yIndex(1),xIndex(1)) = sum(sum(oldImage(yIndex(2):yIndex(3),xIndex(2):xIndex(3)) .* newImage(yIndex(2):yIndex(3),xIndex(2):xIndex(3)))) * imageHeight * imageWidth / numPoint;
end
end
[value, yOffset] = max(corr(margin+1:end-margin,margin+1:end-margin));
[dummy, xOffset] = max(value);
offset = [yOffset(xOffset)+margin-imageHeight xOffset+margin-imageWidth];