I have a image(png format) in hand. The lines that bound the ellipses (represent the nucleus) are over straight which are impractical. How could i extract the lines from the image and make them bent, and with the precondition that they still enclose the nucleus.
The following is the image:
After bending
EDIT: How can i translate the Dilation And Filter part in answer2 into Matlab language? I can't figure it out.
Ok, here is a way involving several randomization steps needed to get a "natural" non symmetrical appearance.
I am posting the actual code in Mathematica, just in case someone cares translating it to Matlab.
(* A preparatory step: get your image and clean it*)
i = Import#"http://i.stack.imgur.com/YENhB.png";
i1 = Image#Replace[ImageData[i], {0., 0., 0.} -> {1, 1, 1}, {2}];
i2 = ImageSubtract[i1, i];
i3 = Inpaint[i, i2]
(*Now reduce to a skeleton to get a somewhat random starting point.
The actual algorithm for this dilation does not matter, as far as we
get a random area slightly larger than the original elipses *)
id = Dilation[SkeletonTransform[
Dilation[SkeletonTransform#ColorNegate#Binarize#i3, 3]], 1]
(*Now the real random dilation loop*)
(*Init vars*)
p = Array[1 &, 70]; j = 1;
(*Store in w an image with a different color for each cluster, so we
can find edges between them*)
w = (w1 =
WatershedComponents[
GradientFilter[Binarize[id, .1], 1]]) /. {4 -> 0} // Colorize;
(*and loop ...*)
For[i = 1, i < 70, i++,
(*Select edges in w and dilate them with a random 3x3 kernel*)
ed = Dilation[EdgeDetect[w, 1], RandomInteger[{0, 1}, {3, 3}]];
(*The following is the core*)
p[[j++]] = w =
ImageFilter[ (* We apply a filter to the edges*)
(Switch[
Length[#1], (*Count the colors in a 3x3 neighborhood of each pixel*)
0, {{{0, 0, 0}, 0}}, (*If no colors, return bkg*)
1, #1, (*If one color, return it*)
_, {{{0, 0, 0}, 0}}])[[1, 1]] (*If more than one color, return bkg*)&#
Cases[Tally[Flatten[#1, 1]],
Except[{{0.`, 0.`, 0.`}, _}]] & (*But Don't count bkg pixels*),
w, 1,
Masking -> ed, (*apply only to edges*)
Interleaving -> True (*apply to all color chanels at once*)]
]
The result is:
Edit
For the Mathematica oriented reader, a functional code for the last loop could be easier (and shorter):
NestList[
ImageFilter[
If[Length[#1] == 1, #1[[1, 1]], {0, 0, 0}] &#
Cases[Tally[Flatten[#1, 1]], Except[{0.` {1, 1, 1}, _}]] & , #, 1,
Masking -> Dilation[EdgeDetect[#, 1], RandomInteger[{0, 1}, {3, 3}]],
Interleaving -> True ] &,
WatershedComponents#GradientFilter[Binarize[id,.1],1]/.{4-> 0}//Colorize,
5]
What you have as input is the Voronoi diagram. You can recalculate it using another distance function instead of the Euclidean one.
Here is an example in Mathematica using the Manhattan Distance (i3 is your input image without the lines):
ColorCombine[{Image[
WatershedComponents[
DistanceTransform[Binarize#i3,
DistanceFunction -> ManhattanDistance] ]], i3, i3}]
Edit
I am working with another algorithm (preliminary result). What do you think?
Here is what I came up with, it is not a direct translation of #belisarius code, but should be close enough..
%# read image (indexed image)
[I,map] = imread('http://i.stack.imgur.com/YENhB.png');
%# extract the blobs (binary image)
BW = (I==1);
%# skeletonization + dilation
BW = bwmorph(BW, 'skel', Inf);
BW = imdilate(BW, strel('square',2*1+1));
%# connected components
L = bwlabel(BW);
imshow(label2rgb(L))
%# filter 15x15 neighborhood
for i=1:13
L = nlfilter(L, [15 15], #myFilterFunc);
imshow( label2rgb(L) )
end
%# result
L(I==1) = 0; %# put blobs back
L(edge(L,'canny')) = 0; %# edges
imshow( label2rgb(L,#jet,[0 0 0]) )
myFilterFunc.m
function p = myFilterFunc(x)
if range(x(:)) == 0
p = x(1); %# if one color, return it
else
p = mode(x(x~=0)); %# else, return the most frequent color
end
end
The result:
and here is an animation of the process:
Related
I would like to implement an unsupervised clustering to detect grids (vertical/horizontal lines) for spatial points.
I have tried DBSCAN and it gives subpar results. It is able to pick out the grids as seen in red below:
However, it is not able to completely pick out all the points that form the vertical/horizontal lines and if i relax the parameters of epsilon, it will incorrectly classify more points as noisy (e.g. the bottom left of the picture).
I was wondering if maybe there is a modification model of DBSCAN that uses ellipse instead of circles? Or any other clustering methods recommended for this that does not need to prespecify the number of clusters?
Or is there a better method to identify these points that make the grid? Any help is appreciated.
You can use an anisotropical DBSCAN by modifying your data this way : value of anisotropy >1 will find vertical clusters and values <1 will find horizontal clusters.
from sklearn.cluster import DBSCAN
def anisotropical_DBSCAN(X, anisotropy, eps, min_samples):
"""ANIsotropic DBSCAN clustering : some documentation would be nice here :)
returns an array with """
X[:, 1] = X[:, 1]*anisotropy
db = DBSCAN(eps=eps, min_samples=min_samples).fit(X)
return db
Here is a full example with data :
import numpy as np
from sklearn.cluster import DBSCAN
from sklearn.datasets import make_blobs
centers = [[1, 1], [-1, -1], [1, -1]]
X, labels_true = make_blobs(
n_samples=750, centers=centers, cluster_std=0.4, random_state=0
)
print(X.shape)
def anisotropical_DBSCAN(X, anisotropy, eps, min_samples):
"""ANIsotropic DBSCAN clustering : some documentation would be nice here :)
returns an array with """
X[:, 1] = X[:, 1]*anisotropy
db = DBSCAN(eps=eps, min_samples=min_samples).fit(X)
return db
db = anisotropical_DBSCAN(X, anisotropy = 0.1, eps = 0.1, min_samples = 10)
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
labels = db.labels_
# Number of clusters in labels, ignoring noise if present.
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
# #############################################################################
# Plot result
import matplotlib.pyplot as plt
# Black removed and is used for noise instead.
unique_labels = set(labels)
colors = [plt.cm.Spectral(each) for each in np.linspace(0, 1, len(unique_labels))]
for k, col in zip(unique_labels, colors):
if k == -1:
# Black used for noise.
col = [0, 0, 0, 1]
class_member_mask = labels == k
xy = X[class_member_mask & core_samples_mask]
plt.plot(
xy[:, 0],
xy[:, 1],
"o",
markerfacecolor=tuple(col),
markeredgecolor="k",
markersize=14,
)
xy = X[class_member_mask & ~core_samples_mask]
plt.plot(
xy[:, 0],
xy[:, 1],
"o",
markerfacecolor=tuple(col),
markeredgecolor="k",
markersize=6,
)
plt.title("Estimated number of clusters: %d" % n_clusters_)
You get vertical clusters :
Now change the parameters to db = anisotropical_DBSCAN(X, anisotropy = 10, eps = 1, min_samples = 10) I had to change eps value because the horizontal scale and vertical scale arent the same, but in your case, you should be able to keep the same (eps, min sample) for detecting lines
And you get horizontal clusters :
There are also implementations of anisotropical DBSCAN that are probably a lot cleaner https://github.com/gissong/ADCN
I have a clean image, and a noisy image. I created a denoiser and applied it to the noisy image, that was my final output. Now to compare how much this image is close to a clean image I need to compare it using PSNR and SSIM, but due to different positions of the image I am unable to compare.
Now I am getting SSIM as 0.5, which is very low, due to the improper placement of both the images. If the images are registered properly, then I guess SSIM should come around 0.80+. But I have not been able to accomplish this.
How can I align these two images to obtain a good SSIM value?
I have two coin images, 1st image (CLEAN), 2nd image (IMPROVED a NOISY IMG), for comparison.
Clean Img:
Noisy Img:
Due to positions of images at different positions ssim(img1,img2) is giving incorrect output. I tried cropping but that did not work.
Here is what I have tried so far:
Attempt 1:
function [valPSNR,valSSIM,badpict]=getSSIM(clean_img,img2)
% pad reference image since object is so close to edges
refpict = padarray(mat2gray(clean_img),[20 20],'replicate','both');
% crop test image down to extract the object alone
badpict = imcrop(mat2gray(img2),[2.5 61.5 357 363]);
% maximize normalized cross-correlation to find offset
szb = size(badpict);
c = normxcorr2(badpict,refpict);
[idxy idxx] = find(c == max(c(:)));
osy = idxy-szb(1);
osx = idxx-szb(2);
% crop the reference pict to the ROI
refpict = refpict(osy:idxy-1,osx:idxx-1);
%imshow(imfuse(badpict,refpict,'checkerboard'));
%imagesc(badpict);
valSSIM=ssim(badpict,refpict);
valPSNR=getPSNR(badpict,refpict);
img2=badpict;
clean_img=refpict;
figure; imshowpair(clean_img,img2);
figure; montage({mat2gray(clean_img),mat2gray(img2)}, 'Size', [1 2], 'BackgroundColor', 'w', 'BorderSize', [2 2]);
end
Attempt 2:
function [valPSNR,valSSIM,badpict]=getSSIM2(clean_img,img2)
% pad reference image since object is so close to edges
bw1 = im2bw(mat2gray(clean_img));
bw2 = imclose(im2bw(mat2gray(img2),0.3),strel('disk',9));
bw2 = bwareafilt(bw2,1);
% make same size
[r,c] = find(bw1);
clean_img = clean_img(min(r):max(r),min(c):max(c));
[r,c] = find(bw2);
img2 = img2(min(r):max(r),min(c):max(c));
img2= imresize(img2, size(clean_img),'bilinear');
valPSNR=getPSNR(mat2gray(clean_img),mat2gray(img2));
valSSIM=ssim(mat2gray(clean_img),mat2gray(img2));
badpict=img2;
figure; imshowpair(clean_img,img2);
figure; montage({mat2gray(clean_img),mat2gray(img2)}, 'Size', [1 2], 'BackgroundColor', 'w', 'BorderSize', [2 2]);
end
As others have pointed out, the resampling required by registration will have some non-zero error. But, here is some sample code that will take you through the registration part that is the crux of your question.
% SSIM isn't defined on RGB images, convert to grayscale.
ref = rgb2gray(imread('https://i.stack.imgur.com/tPKEJ.png'));
X = rgb2gray(imread('https://i.stack.imgur.com/KmU4y.png'));
% The input image data has bright borders at the edges that create
% artifacts in resampling, best to just crop those or maybe there are
% aquisitions that don't have these borders?
X = X(3:end-2,3:end-2);
ref = ref(4:end-3,4:end-3);
figure
montage({X,ref});
tform = imregcorr(X,ref,"translation");
Xreg = imwarp(X,tform,OutputView=imref2d(size(ref)),SmoothEdges=true);
figure
imshowpair(Xreg,ref)
ssim(Xreg,ref)
Maybe you can refer to my github.
I implemented a template matching algorithm by OpenCV which you can use NCC-Based Pattern Matching to find targets, and then get a score (similarity).
You can then use this score to decide if it is clean.
Besides, tranforming c++ code may be an issue for you, but just find the all corresponded function in matlab version.
Here are effects (red blocks are areas with similarity higher than threshold 0.85 in comparison with golden sample):
The whole function is too long to be posted here.
Part of the function:
for (int i = 0; i < iSize; i++)
{
Mat matRotatedSrc, matR = getRotationMatrix2D (ptCenter, vecAngles[i], 1);
Mat matResult;
Point ptMaxLoc;
double dValue, dMaxVal;
double dRotate = clock ();
Size sizeBest = GetBestRotationSize (vecMatSrcPyr[iTopLayer].size (), pTemplData->vecPyramid[iTopLayer].size (), vecAngles[i]);
float fTranslationX = (sizeBest.width - 1) / 2.0f - ptCenter.x;
float fTranslationY = (sizeBest.height - 1) / 2.0f - ptCenter.y;
matR.at<double> (0, 2) += fTranslationX;
matR.at<double> (1, 2) += fTranslationY;
warpAffine (vecMatSrcPyr[iTopLayer], matRotatedSrc, matR, sizeBest);
MatchTemplate (matRotatedSrc, pTemplData, matResult, iTopLayer);
minMaxLoc (matResult, 0, &dMaxVal, 0, &ptMaxLoc);
vecMatchParameter[i * (m_iMaxPos + MATCH_CANDIDATE_NUM)] = s_MatchParameter (Point2f (ptMaxLoc.x - fTranslationX, ptMaxLoc.y - fTranslationY), dMaxVal, vecAngles[i]);
for (int j = 0; j < m_iMaxPos + MATCH_CANDIDATE_NUM - 1; j++)
{
ptMaxLoc = GetNextMaxLoc (matResult, ptMaxLoc, -1, pTemplData->vecPyramid[iTopLayer].cols, pTemplData->vecPyramid[iTopLayer].rows, dValue, m_dMaxOverlap);
vecMatchParameter[i * (m_iMaxPos + MATCH_CANDIDATE_NUM) + j + 1] = s_MatchParameter (Point2f (ptMaxLoc.x - fTranslationX, ptMaxLoc.y - fTranslationY), dValue, vecAngles[i]);
}
}
FilterWithScore (&vecMatchParameter, m_dScore-0.05*iTopLayer);
Lets say I have an NxM weight variable weights and a constant NxM matrix of 1s and 0s mask.
If a layer of my network is defined like this (with other layers similarly defined):
masked_weights = mask*weights
layer1 = tf.relu(tf.matmul(layer0, masked_weights) + biases1)
Will this network behave as if the corresponding 0s in mask are zeros in weights during training? (i.e. as if the connections represented by those weights had been removed from the network entirely)?
If not, how can I achieve this goal in TensorFlow?
The answer is yes. The experiment depicts the following graph.
The implementation is:
import numpy as np, scipy as sp, tensorflow as tf
x = tf.placeholder(tf.float32, shape=(None, 3))
weights = tf.get_variable("weights", [3, 2])
bias = tf.get_variable("bias", [2])
mask = tf.constant(np.asarray([[0, 1], [1, 0], [0, 1]], dtype=np.float32)) # constant mask
masked_weights = tf.multiply(weights, mask)
y = tf.nn.relu(tf.nn.bias_add(tf.matmul(x, masked_weights), bias))
loss = tf.losses.mean_squared_error(tf.constant(np.asarray([[1, 1]], dtype=np.float32)),y)
weights_grad = tf.gradients(loss, weights)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
print("Masked weights=\n", sess.run(masked_weights))
data = np.random.rand(1, 3)
print("Graident of weights\n=", sess.run(weights_grad, feed_dict={x: data}))
sess.close()
After running the code above, you will see the gradients are masked as well. In my example, they are:
Graident of weights
= [array([[ 0. , -0.40866762],
[ 0.34265977, -0. ],
[ 0. , -0.35294518]], dtype=float32)]
The answer is yes and the reason lies in backpropogation as explained below.
mask_w = mask * w
del(mask_w) = mask * del(w).
The mask will make the gradient 0 wherever its value is zero. Wherever its value is 1, gradient will flow as previously. This is a common trick used in seq2seq predictions to mask the different size output in decoding layer. You can read more about this here.
I have a set of vectors (length of 50, essentially a set of curves) that i want to try to match another single curve(vector) and obtain the coefficients of each of the vectors in the first set to match the second curve. The coefficients need to be >= 0.0 . I.e, a linear combination of the first set of curves to match the single curve. Any help in which direction I should go would be helpful.
If I understand correctly, you have a set of curves
each of which you want to multiply with a scaling factor, so that it reproduces some target curve
as closely as possible.
This is easily done with a linear least squares approximation.
%# create some sample curves
x = -10:0.1:10;
g1 = exp(-(x-3).^2/4);
g2 = exp(-(x-0).^2/4);
g3 = exp(-(x+2).^2/4);
%# make a target curve, corrupt with noise
y = 2*g1+4*g2+g3+randn(size(x))*0.2;
%# use the `ldivide` operator to solve an equation of the form
%# A*x=B
%# so that x (=fact here) is x=A^-1*B or, in Matlab terms, A\B
%# note the transposes, A should be a n-by-3 array, B a n-by-1 array
%# so that x is a 3-by-1 array of factors
fact = [g1;g2;g3]'\y'
fact =
1.9524
3.9978
1.0105
%# Show the result
figure,plot(x,y)
hold on,plot(x,fact(1)*g1+fact(2)*g2+fact(3)*g3,'m')
so thats what he meant!..
mathematica version..
x = Table[i, {i, -10, 10, .1}];
basis = {
Exp[-(# - 3)^2/4] & /# x,
Exp[-(# - 0)^2/4] & /# x,
Exp[-(# + 2)^2/4] & /# x
};
Show[
ListPlot[Table[{x[[i]], #[[i]]}, {i, Length[x]}] ,
Joined -> True , PlotStyle -> Hue [Random[]]] & /# basis ]
y = Table [ 2 basis[[1, i]] + 4 basis[[2, i]] + basis[[3, i]] +
RandomReal[{.5, .5}] ,{i, Length[x]}];
dataplot = ListPlot[Table[{x[[i]], y[[i]]}, {i, Length[x]}] ]
mathematica does not magically do least squares if you simply solve an underdetermined system, so find a least squres result explicitly:
coefs = FindMinimum[
Total[(#^2 & /# (Sum[a[k] basis[[k]] , {k, Length[basis]}]-y) )],
Array[a, Length[basis]]][[2]]
Show[dataplot,
ListPlot[i = 0; {x[[++i]], #} & /#
(Sum[a[k] basis[[k]] , {k, 3}] /. coefs),
Joined -> True]]
note if you want ot restrict the coefficents to be >= 0 as stated you can simply square the values in the formulation like this:
coefs = FindMinimum[
Total[(#^2 & /#
(Sum[a[k]^2 basis[[k]] , {k, Length[basis]}]-y) )],
Array[a, Length[basis]]][[2]]
Show[dataplot,
ListPlot[i = 0; {x[[++i]], #} & /#
(Sum[a[k]^2 basis[[k]] , {k, 3}] /. coefs),
Joined -> True]]
you will get predictably poor results if the actual best fit wants to have a negative value.
I'm not too sure if this is possible, but my understanding of MATLAB could certainly be better.
I have some code I wish to vectorize as it's causing quite a bottleneck in my program. It's part of an optimisation routine which has many possible configurations of Short Term Average (STA), Long Term Average (LTA) and Sensitivity (OnSense) to run through.
Time is in vector format, FL2onSS is the main data (an Nx1 double), FL2onSSSTA is its STA (NxSTA double), FL2onSSThresh is its Threshold value (NxLTAxOnSense double)
The idea is to calculate a Red alarm matrix which will be 4D - the alarmStatexSTAxLTAxOnSense that is used throughout the rest of the program.
Red = zeros(length(FL2onSS), length(STA), length(LTA), length(OnSense), 'double');
for i=1:length(STA)
for j=1:length(LTA)
for k=1:length(OnSense)
Red(:,i,j,k) = calcRedAlarm(Time, FL2onSS, FL2onSSSTA(:,i), FL2onSSThresh(:,j,k));
end
end
end
I've currently got this repeating a function in an attempt to get a bit more speed out of it, but obviously it will be better if the entire thing can be vectorised. In other words I do not need to keep the function if there is a better solution.
function [Red] = calcRedAlarm(Time, FL2onSS, FL2onSSSTA, FL2onSSThresh)
% Calculate Alarms
% Alarm triggers when STA > Threshold
zeroSize = length(FL2onSS);
%Precompose
Red = zeros(zeroSize, 1, 'double');
for i=2:zeroSize
%Because of time chunks being butted up against each other, alarms can
%go off when they shouldn't. To fix this, timeDiff has been
%calculated to check if the last date is different to the current by 5
%seconds. If it isn't, don't generate an alarm as there is either a
%validity or time gap.
timeDiff = etime(Time(i,:), Time(i-1,:));
if FL2onSSSTA(i) > FL2onSSThresh(i) && FL2onSSThresh(i) ~= 0 && timeDiff == 5
%If Short Term Avg is > Threshold, Trigger
Red(i) = 1;
elseif FL2onSSSTA(i) < FL2onSSThresh(i) && FL2onSSThresh(i) ~= 0 && timeDiff == 5
%If Short Term Avg is < Threshold, Turn off
Red(i) = 0;
else
%Otherwise keep current state
Red(i) = Red(i-1);
end
end
end
The code is simple enough so I won't explain it any further. If you need elucidation on what a particular line is doing, let me know.
The trick is to bring all your data to the same form, using mostly repmat and permute. Then the logic is the simple part.
I needed a nasty trick to implement the last part (if none of the conditions hold, use the last results). usually that sort of logic is done using a cumsum. I had to use another matrix of 2.^n to make sure the values that are defined are used (so that +1,+1,-1 will really give 1,1,0) - just look at the code :)
%// define size variables for better readability
N = length(Time);
M = length(STA);
O = length(LTA);
P = length(OnSense);
%// transform the main data to same dimentions (3d matrices)
%// note that I flatten FL2onSSThresh to be 2D first, to make things simpler.
%// anyway you don't use the fact that its 3D except traversing it.
FL2onSSThresh2 = reshape(FL2onSSThresh, [N, O*P]);
FL2onSSThresh3 = repmat(FL2onSSThresh2, [1, 1, M]);
FL2onSSSTA3 = permute(repmat(FL2onSSSTA, [1, 1, O*P]), [1, 3, 2]);
timeDiff = diff(datenum(Time))*24*60*60;
timeDiff3 = repmat(timeDiff, [1, O*P, M]);
%// we also remove the 1st plain from each of the matrices (the vector equiv of running i=2:zeroSize
FL2onSSThresh3 = FL2onSSThresh3(2:end, :, :);
FL2onSSSTA3 = FL2onSSSTA3(2:end, :, :);
Red3 = zeros(N-1, O*P, M, 'double');
%// now the logic in vector form
%// note the chage of && (logical operator) to & (binary operator)
Red3((FL2onSSSTA3 > FL2onSSThresh3) & (FL2onSSThresh3 ~= 0) & (timeDiff3 == 5)) = 1;
Red3((FL2onSSSTA3 < FL2onSSThresh3) & (FL2onSSThresh3 ~= 0) & (timeDiff3 == 5)) = -1;
%// now you have a matrix with +1 where alarm should start, and -1 where it should end.
%// add the 0s at the begining
Red3 = [zeros(1, O*P, M); Red3];
%// reshape back to the same shape
Red2 = reshape(Red3, [N, O, P, M]);
Red2 = permute(Red2, [1, 4, 2, 3]);
%// and now some nasty trick to convert the start/end data to 1 where alarm is on, and 0 where it is off.
Weights = 2.^repmat((1:N)', [1, M, O, P]); %// ' damn SO syntax highlighting. learn MATLAB already!
Red = (sign(cumsum(Weights.*Red2))+1)==2;
%// and we are done.
%// print sum(Red(:)!=OldRed(:)), where OldRed is Red calculated in non vector form to test this.