Before I begin, I would just like to clarify that I understand how to use kernels and the conv2() function in order to mean filter an image, but I have been tasked with doing this manually by calling up each pixel.
My goal here is to call up each pixel, find its neighbors, average them out, and replace previous values with the acquired mean, without using kernels or conv2(). I have so far attempted to find the neighbors of each pixel using
for
I = 1:512;
for
J = 1:683;
A = myimage;
neighbor_offsets = [-1, A, 1, -A, A + 1, -A + 1, -A-1, A - 1];
idx = [I J];
neighbors = bsxfun(#plus,idx,neighbor_offsets);
but it does not seem to work, and I am a bit lost in trying to fix it. I think I could finish the job if I were able to get the neighbors by using something like
sum(neighbors) / 9
then replace the previous values with that answer, but please correct me if I'm. I've developed somewhat of a tendency to ask poor questions, so if anything is unclear, please let me know so I can clarify for you. Thanks
Example below treats pixels at the edge in the manner that it only considers pixels that are inside the image. For example when program is computing average with kernel dy = (-1:1) and dx = (-1:1) on top-left corner, it only considers top-left corner and its immediate 3 neighbors (right, right-bottom, right), and does average of those 4 pixels.
I would strongly advise you to test every line separately in Matlab's command window to see it's behavior!
% find image size
imsz = size( myimage );
% initialize output image
imavg = zeros( imsz );
% iterate over pixels
for yy = 1 : imsz(1)
for xx = 1 : imsz(2)
% define rectangle-kernel width
dy = (-1:1); % 1 up, to 1 down and ...
dx = (-1:1); % 1 left, to 1 right from current pixel
% get indexes of image
indy = yy + dy;
indx = xx + dx;
% [!!!] keep indexes that are inside image
indy = indy( indy>0 & indy<=imsz(1) );
indx = indx( indx>0 & indx<=imsz(2) );
% create all the pairings of chosen indexes
[ IY, IX ] = meshgrid( indy, indx );
% take all values of chosen pixels
pixs = myimage( sub2ind(imsz,IY(:),IX(:)) );
% save mean of chosen pixels to the given location
imavg(yy,xx) = mean( pixs );
end
end
You can create function from the above code with creating mean_filter.m file with this contents:
function imagv = mean_filter( myimage )
% code from above ...
You can call function from command window by positioning yourself in the directory where it's at and executing filtered = mean_filter( myimage );.
You can repeatedly filter the same image with:
filtered_3_times = myimage;
for ii = 1 : 3
filtered_3_times = mean_filter( filtered_3_times );
end
Related
Using the answer found here, I was able to draw a nice torus. I would like to draw two "circles" on this torus, the first of which is standard. The second should be somewhat random, perhaps like a ladder, ideally on the opposite side from the first. It might be kind of jagged, but should remain on the edges of the lattice and should connect back to itself. Here is what I have so far
am = 1.;
rm = 3.;
t = linspace(-pi,pi,16);
p = linspace(0.,2.*pi,16);
[t,p] = meshgrid(p,t);
x = (rm + am.*cos(p)).*cos(t);
y = (rm + am.*cos(p)).*sin(t);
z = am.*sin(p);
hsurf = surf(x,y,z);
axis equal;
set(hsurf, 'FaceColor','interp','FaceAlpha',0.5,'EdgeAlpha',0.25);
hold on
plot3((rm+am.*cos(p)).*cos(t(8)),(rm+am.*cos(p)).*sin(t(8)),am.*sin(p), 'b', 'LineWidth',2);
I have the standard loop, but can't get the jagged one. I have been trying to specify the other loop by specifying the vertices, but it doesn't seem to be working. Can I specify a curve on this mesh by specifying its vertices? If not, are there any suggestions? Thanks for any help you can provide.
EDIT: Sorry for being vague earlier. The picture below shows what I would like. Note that the right circle is given by the above code, whereas the left hand 'circle' is drawn by hand, and is what I would like to automate.
If you want the vertices of the (jagged or not) circle to match the points of the surface, you do not need to recalculate any points. You can simply re-use some of the points of your surface, choosing the path which define the shape you want.
I'll use your code to define the initial tore, with just a slight modification: I'll use the upper-case letter for variable which define a matrix (and lower case for variables which are simple 1D vector). So your initial tore becomes:
%% // define initial tore
am = 1.; rm = 3.;
t = linspace(-pi,pi,16);
p = linspace(0,2.*pi,16);
[T,P] = meshgrid(p,t);
X = (rm + am.*cos(P)).*cos(T);
Y = (rm + am.*cos(P)).*sin(T);
Z = am.*sin(P);
hsurf = surf(X,Y,Z,'FaceColor','interp','FaceAlpha',0.5,'EdgeAlpha',0.25);
axis equal ; hold on
Not a big deal but this allow to have only 1D vector coordinates in the case where you define your circle by equation as you were doing. It keeps your variables and workspace cleaner and reduce the risk of mistakes and dimension errors.
For the simple circle, you could redefine the coordinate by equation as you did (developed here for clarity)
%% // your simple circle - calculated
colID = 8 ;
xs = (rm+am.*cos(p)).*cos(t(colID)) ;
ys = (rm+am.*cos(p)).*sin(t(colID)) ;
zs = am.*sin(p) ;
hp = plot3(xs,ys,zs, 'b', 'LineWidth',2);
%// This was generating 2D arrays (matrices) for xs, ys and zs when you
%// were using the meshed version of T and P. Using only the vector version of
%// 't' and 'p' generate only vector coordinates. => cleaner and safer.
But I'd like to bring your attention to another way, just use the points of the surface already defined. This (i) eliminates the need for new calculations, and (ii) insure that the vertices of the circle will be matching exactly some vertices of the tore.
%% // another simple circle - extracted from the tore vertices
colID = 8 ;
xs = X(:,colID) ;
ys = Y(:,colID) ;
zs = Z(:,colID) ;
hpc = plot3(xs,ys,zs, 'b', 'LineWidth',2);
Again, there are extra lines for clarity but you can compact the code once you understand what is going on. These 2 different ways of producing a circle result in the following figures:
For illustration purpose, I highlighted the vertices of the tore (small black dots), so you can see if the circles vertices match or not.
Now for the jagged circle, the last method presented will have even more interest. As you observed, to get a circle coordinate we simply extracted one column of each of the tore matrix coordinates. We used a fixed value for the colID to retrieve. To "jagg" your circle, we just need to introduce a small perturbation in the column number, so we will occasionally retrieve the adjacent vertex coordinates.
The following code generate a column ID varying around the staring column ID. You can define the maximum total spread as well as the maximum single increment:
%% // generate index of columns to be retrieved
idxcol = zeros(size(X,1),1) ;
maxDeviation = 5 ; %// total maximum deviation to the initial center line
maxPerturbation = 2 ; %// max deviation for 1 increment
deviation = cumsum(randi([-maxPerturbation maxPerturbation],size(X,1),1)) ;
%// bound deviations to maximum values
deviation(deviation>maxDeviation) = maxDeviation ;
deviation(deviation<-maxDeviation) = -maxDeviation ;
startColID = 8 ;
colID = startColID + deviation ;
%// close the profile by repeating first point in the end if it's not closed already
if colID(end) ~= colID(1) ; colID(end) = colID(1) ; end
If we now use coordinate from theses columns, we get:
npt = size(colID,1) ;
xcj = zeros(npt) ; ycj = xcj ; zcj = xcj ;
for k=1:npt
xcj(k) = X( k , colID(k) ) ;
ycj(k) = Y( k , colID(k) ) ;
zcj(k) = Z( k , colID(k) ) ;
end
hpc = plot3(xcj,ycj,zcj, 'b', 'LineWidth',2);
It is a closed profile turning around the tore but the lines do not match the lines of the surface as in your drawn example. This is because the transition from one column ID to another happen at each change of x index.
To rectify that, we can use the stairs function:
%% // create a stepped profile
[xd,yd] = stairs(colID) ;
%% // display rectified jagged circle
npt = size(yd,1) ;
xcj = zeros(npt) ; ycj = xcj ; zcj = xcj ;
for k=1:npt
xcj(k) = X( xd(k) , yd(k) ) ;
ycj(k) = Y( xd(k) , yd(k) ) ;
zcj(k) = Z( xd(k) , yd(k) ) ;
end
hpc = plot3(xcj,ycj,zcj, 'b', 'LineWidth',2);
This will now generate the following profile:
Much better ... However, when the column ID moves by more than one index for the same x, we still miss anchor points to keep the jagged circle profile matching the tore profile. If your basic column deviation increment is more than one, you will even have many more of these artefact.
To completely rectify that, we need to add the missing anchor points. I couldn't find a built-in function to do that so I went the good old loop way. Feel free to optimize if you find some way.
%% // recreate index vectors with all the necessary anchor points
colX(1,1) = xd(1) ;
colY(1,1) = yd(1) ;
k = 2 ;
for t=2:size(yd,1)
inc = sign(yd(t)-yd(t-1)) ; %// define increment of 1 with correct sign
ids = yd(t-1):inc:yd(t)-inc ; %// range of index to be covered
if isempty(ids) ; ids = yd(t) ; end %// catch corner cases
%// now add these points to the list
n = length(ids) ;
colX(k:k+n-1,1) = xd(t-1) ;
colY(k:k+n-1,1) = ids ;
k=k+n;
end
%// close profile (add last point in the cases where it is necessary)
if inc ~= 0
colX(end+1,1) = xd(end) ;
colY(end+1,1) = yd(end) ;
end
%% // display fully rectified jagged circle
npt = size(colX,1) ;
xcj = zeros(npt) ; ycj = xcj ; zcj = xcj ;
for k=1:npt
xcj(k) = X( colX(k) , colY(k) ) ;
ycj(k) = Y( colX(k) , colY(k) ) ;
zcj(k) = Z( colX(k) , colY(k) ) ;
end
And now we don't have gaps between two column ID any more, all the points of the jagged circle match a point of the surface profile:
Following is my code :
function marks(my_numbers)
handle = zeros(5,1)
x = 10 ;
y = 10:20:100 ;
for i = 1
for j = 1:5 ;
handle(j,i) = rectangle('position',[x(i),y(j),20 10],'facecolor','r')
end
end
end
now lets say input argument my_numbers = 2
so i have written the code :
set(handle(j(my_numbers),1),'facecolor','g')
With this command, rectangle with lower left corner at (30,10) should have turned green. But MATLAB gives an error of index exceeds matrix dimensions
This is more an illustrated comment than an answer, but as #hagubear mentioned your i index is pointless so you could remove it altogether.
Using set(handle(my_numbers,1),'facecolor','g') will remove the error, because you were trying to access handles(j(2),1) and that was not possible because j is a scalar.
Anyhow using this line after your plot works fine:
set(handle(my_numbers,1),'facecolor','g')
According to your comment below, here is a way to call the function multiple times and add green rectangles as you go along. There are 2 files for the purpose of demonstration, the function per se and a script to call the function multiple times and generate an animated gif:
1) The function:
function marks(my_numbers)
%// Get green and red rectangles to access their properties.
GreenRect = findobj('Type','rectangle','FaceColor','g');
RedRect = findobj('Type','rectangle');
%// If 1st call to the function, create your plot
if isempty(RedRect)
handle = zeros(5,1);
x = 10 ;
y = 10:20:100 ;
for j = 1:5 ;
handle(j) = rectangle('position',[x,y(j),20 10],'facecolor','r');
end
set(handle(my_numbers,1),'facecolor','g')
%// If not 1st call, fetch existing green rectangles and color them green. Then color the appropriate rectangle given by my_numbers.
else
RedRect = flipud(RedRect); %// Flip them to maintain correct order
if numel(GreenRect) > 0
hold on
for k = numel(GreenRect)
set(GreenRect(k),'facecolor','g')
set(RedRect(my_numbers,1),'facecolor','g')
end
end
end
2) The script:
clear
clc
%// Shuffle order for appearance of green rectangles.
iter = randperm(5);
filename = 'MyGifFile.gif';
for k = iter
marks(k)
pause(1)
frame = getframe(1);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if k == iter(1)
imwrite(imind,cm,filename,'gif', 'Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
end
Here is the animated gif of the output:
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:
Hello again logical friends!
I’m aware this is quite an involved question so please bear with me! I think I’ve managed to get it down to two specifics:- I need two loops which I can’t seem to get working…
Firstly; The variable rollers(1).ink is a (12x1) vector containing ink values. This program shares the ink equally between rollers at each connection. I’m attempting to get rollers(1).ink to interact with rollers(2) only at specific timesteps. The ink should transfer into the system once for every full revolution i.e. nTimesSteps = each multiple of nBins_max. The ink should not transfer back to rollers(1).ink as the system rotates – it should only introduce ink to the system once per revolution and not take any back out. Currently I’ve set rollers(1).ink = ones but only for testing. I’m truly stuck here!
Secondly; The reason it needs to do this is because at the end of the sim I also wish to remove ink in the form of a printed image. The image should be a reflection of the ink on the last roller in my system and half of this value should be removed from the last roller and taken out of the system at each revolution. The ink remaining on the last roller should be recycled and ‘re-split’ in the system ready for the next rotation.
So…I think it’s around the loop beginning line86 where I need to do all this stuff. In pseudo, for the intermittent in-feed I’ve been trying something like:
For k = 1:nTimeSteps
While nTimesSteps = mod(nTimeSteps, nBins_max) == 0 % This should only output when nTimeSteps is a whole multiple of nBins_max i.e. one full revolution
‘Give me the ink on each segment at each time step in a matrix’
End
The output for averageAmountOfInk is the exact format I would like to return this data except I don’t really need the average, just the actual value at each moment in time. I keep getting errors for dimensional mismatches when I try to re-create this using something like:
For m = 1:nTimeSteps
For n = 1:N
Rollers(m,n) = rollers(n).ink’;
End
End
I’ll post the full code below if anyone is interested to see what it does currently. There’s a function at the end also which of course needs to be saved out to a separate file.
I’ve posted variations of this question a couple of times but I’m fully aware it’s quite a tricky one and I’m finding it difficult to get my intent across over the internets!
If anyone has any ideas/advice/general insults about my lack of programming skills then feel free to reply!
%% Simple roller train
% # Single forme roller
% # Ink film thickness = 1 micron
clc
clear all
clf
% # Initial state
C = [0,70; % # Roller centres (x, y)
10,70;
21,61;
11,48;
21,34;
27,16;
0,0
];
R = [5.6,4.42,9.8,6.65,10.59,8.4,23]; % # Roller radii (r)
% # Direction of rotation (clockwise = -1, anticlockwise = 1)
rotDir = [1,-1,1,-1,1,-1,1]';
N = numel(R); % # Amount of rollers
% # Find connected rollers
isconn = #(m, n)(sum(([1, -1] * C([m, n], :)).^2)...
-sum(R([m, n])).^2 < eps);
[Y, X] = meshgrid(1:N, 1:N);
conn = reshape(arrayfun(isconn, X(:), Y(:)), N, N) - eye(N);
% # Number of bins for biggest roller
nBins_max = 50;
nBins = round(nBins_max*R/max(R))';
% # Initialize roller struct
rollers = struct('position',{}','ink',{}','connections',{}',...
'rotDirection',{}');
% # Initialise matrices for roller properties
for ii = 1:N
rollers(ii).ink = zeros(1,nBins(ii));
rollers(ii).rotDirection = rotDir(ii);
rollers(ii).connections = zeros(1,nBins(ii));
rollers(ii).position = 1:nBins(ii);
end
for ii = 1:N
for jj = 1:N
if(ii~=jj)
if(conn(ii,jj) == 1)
connInd = getConnectionIndex(C,ii,jj,nBins(ii));
rollers(ii).connections(connInd) = jj;
end
end
end
end
% # Initialize averageAmountOfInk and calculate initial distribution
nTimeSteps = 1*nBins_max;
averageAmountOfInk = zeros(nTimeSteps,N);
inkPerSeg = zeros(nTimeSteps,N);
for ii = 1:N
averageAmountOfInk(1,ii) = mean(rollers(ii).ink);
end
% # Iterate through timesteps
for tt = 1:nTimeSteps
rollers(1).ink = ones(1,nBins(1));
% # Rotate all rollers
for ii = 1:N
rollers(ii).ink(:) = ...
circshift(rollers(ii).ink(:),rollers(ii).rotDirection);
end
% # Update all roller-connections
for ii = 1:N
for jj = 1:nBins(ii)
if(rollers(ii).connections(jj) ~= 0)
index1 = rollers(ii).connections(jj);
index2 = find(ii == rollers(index1).connections);
ink1 = rollers(ii).ink(jj);
ink2 = rollers(index1).ink(index2);
rollers(ii).ink(jj) = (ink1+ink2)/2;
rollers(index1).ink(index2) = (ink1+ink2)/2;
end
end
end
% # Calculate average amount of ink on each roller
for ii = 1:N
averageAmountOfInk(tt,ii) = sum(rollers(ii).ink);
end
end
image(5:20) = (rollers(7).ink(5:20))./2;
inkPerSeg1 = [rollers(1).ink]';
inkPerSeg2 = [rollers(2).ink]';
inkPerSeg3 = [rollers(3).ink]';
inkPerSeg4 = [rollers(4).ink]';
inkPerSeg5 = [rollers(5).ink]';
inkPerSeg6 = [rollers(6).ink]';
inkPerSeg7 = [rollers(7).ink]';
This is an extended comment rather than a proper answer, but the comment box is a bit too small ...
Your code overwhelms me, I can't see the wood for the trees. I suggest that you eliminate all the stuff we don't need to see to help you with your immediate problem (all those lines drawing figures for example) -- I think it will help you to debug your code yourself to put all that stuff into functions or scripts.
Your code snippet
For k = 1:nTimeSteps
While nTimesSteps = mod(nTimeSteps, nBins_max) == 0
‘Give me the ink on each segment at each time step in a matrix’
End
might be (I don't quite understand your use of the while statement, the word While is not a Matlab keyword, and as you have written it the value returned by the statement doesn't change from iteration to iteration) equivalent to
For k = 1:nBins_max:nTimeSteps
‘Give me the ink on each segment at each time step in a matrix’
End
You seem to have missed an essential feature of Matlab's colon operator ...
1:8 = [1 2 3 4 5 6 7 8]
but
1:2:8 = [1 3 5 7]
that is, the second number in the triplet is the stride between successive elements.
Your matrix conn has a 1 at the (row,col) where rollers are connected, and a 0 elsewhere. You can find the row and column indices of all the 1s like this:
[ri,ci] = find(conn==1)
You could then pick up the (row,col) locations of the 1s without the nest of loops and if statements that begins
for ii = 1:N
for jj = 1:N
if(ii~=jj)
if(conn(ii,jj) == 1)
I could go on, but won't, that's enough for one comment.
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