how to calculate Otsu threshold in 1D - threshold

I'm trying to identify bimodal distributions in my analytical chemistry data. Each data set is a list of 3~70 retention times for a particular compound from the GC-MS. RTs for some compound are bimodally distributed where the library searches have assigned the same identity to two or more different features in the data with different RTs. This is quite common for isomers and other compound pairs with very similar mass spectra.
Eg. here's a histogram of RTs for one compound showing bimodal distribution.
I want to calculate the Otsu threshold to try and define bimodal data (there's also multimodal distributions but one step at a time). I'm struggling to understand the Wikipedia article on the calculations but the text indicates that the threshold can be found by finding the minimum intraclass variance. So I've tried computing this from a list of the RTs as follows:
a = list(d['Component RT'])
n = len(a)
b = [a.pop(0)]
varA = []
varB = []
for i in range(1,n-2):
b.append(a.pop(0))
varA.append(statistics.stdev(a)**2)
varB.append(statistics.stdev(b)**2)
Am I right in thinking that if I plot the sum of the variances for the above data I should be able to identify the Otsu threshold as the minimum?
In this example the threshold is obvious and there's about 35 values to work from. For most compounds there's fewer values (typically <15) and the data may be less well defined. Is this even the right threshold to use? The Wikipedia article on modality indicates a whole bunch of other tests for multimodality.

result is simillar to opencv thresh by OTSU.
uchar OTSU(const std::vector<uchar>& input_vec) {
// normalize input to 0-255 if needed
int count[256];
double u0, u1, u;
double pixelSum0, pixelSum1;
int n0, n1;
int bestThresold = 0, thresold = 0;
double w0, w1;
double variable = 0, maxVariable = 0;
for (int i = 0; i < 256; i++)
count[i] = 0;
for (int i = 0; i < input_vec.size(); i++) {
count[int(input_vec[i])] ++;
}
for (thresold = 0; thresold < 256; thresold++) {
n0 = 0;
n1 = 0;
w0 = 0;
w1 = 0;
pixelSum0 = 0;
pixelSum1 = 0;
for (int i = 0; i < thresold; i++) {
n0 += count[i];
pixelSum0 += i * count[i];
}
for (int i = thresold; i < 256; i++) {
n1 += count[i];
pixelSum1 += i * count[i];
}
w0 = double(n0) / (input_vec.size());
w1 = double(n1) / (input_vec.size());
u0 = pixelSum0 / n0;
u1 = pixelSum1 / n1;
u = u0 * w0 + u1 * w1;
variable = w0 * pow((u0 - u), 2) + w1 * pow((u1 - u), 2);
if (variable > maxVariable) {
maxVariable = variable;
bestThresold = thresold;
}
}
return bestThresold;}
ref :https://github.com/1124418652/edge_extract/blob/master/edge_extract/OTSU.cpp

Related

Is there any optimization function in Rcpp

The following is my Rcpp code, and I want to minimize the objective function logtpoi(x,theta) respect to theta in R by 'nlminb'. I found it is slow.
I have two question:
Anyone can improve my Rcpp code? Thank you very much.
Is there any optimization functions in Rcpp? If yes,maybe I can use them in Rcpp directly. And how to use them? Thank you very much.
My code:
#include <RcppArmadillo.h>
using namespace Rcpp;
using namespace arma;
// [[Rcpp::depends("RcppArmadillo")]]
// [[Rcpp::export]]
List dtpoi0(const IntegerVector& x, const NumericVector& theta){
//x is 3-dim vector; theta is a 6-dim parameter vector.
//be careful the order of theta1,...,theta6.
double theta1 = theta[0]; double theta2 = theta[1];
double theta3 = theta[2]; double theta4 = theta[3];
double theta5 = theta[4]; double theta6 = theta[5];
int x1 = x[0]; int x2 = x[1]; int x3 = x[2];
IntegerVector z1 = IntegerVector::create(x1,x2);
IntegerVector z2 = IntegerVector::create(x1,x3);
IntegerVector z3 = IntegerVector::create(x2,x3);
int s1 = min(z1); int s2 = min(z2); int s3 = min(z3);
arma::imat missy(1,3,fill::zeros); arma::irowvec ijk={0,0,0};
for (int i = 0; i <= s1; ++i) {
for (int j = 0; j <= s2; ++j) {
for (int k = 0; k <= s3; ++k) {
if ((i+j <= s1) & (i+k <= s2) & ( j+k <= s3))
{ ijk = {i,j,k};
missy = join_cols(missy,ijk);}
}
}
}
IntegerMatrix misy = as<IntegerMatrix>(wrap(missy));
IntegerVector u1 = IntegerVector::create(0);
IntegerVector u2 = IntegerVector::create(0);
IntegerVector u3 = IntegerVector::create(0);
IntegerVector u4 = IntegerVector::create(0);
IntegerVector u5 = IntegerVector::create(0);
IntegerVector u6 = IntegerVector::create(0);
int total = misy.nrow();
double fvalue = 0;
NumericVector part1(1); NumericVector part2(1);
NumericVector part3(1); NumericVector part4(1);
NumericVector part5(1); NumericVector part6(1);
for (int l = 1; l < total; ++l) {
u1 = IntegerVector::create(x1-misy(l,0)-misy(l,1));
u2 = IntegerVector::create(x2-misy(l,0)-misy(l,2));
u3 = IntegerVector::create(x3-misy(l,1)-misy(l,2));
u4 = IntegerVector::create(misy(l,0));
u5 = IntegerVector::create(misy(l,1));
u6 = IntegerVector::create(misy(l,2));
part1 = dpois(u1,theta1);
part2 = dpois(u2,theta2);
part3 = dpois(u3,theta3);
part4 = dpois(u4,theta4);
part5 = dpois(u5,theta5);
part6 = dpois(u6,theta6);
fvalue = fvalue + (part1*part2*part3*part4*part5*part6)[0]; }
return(List::create(Named("misy") = misy,Named("fvalue") = fvalue));
}
// [[Rcpp::export]]
NumericVector dtpoi(const IntegerMatrix& x, const NumericVector& theta){
//x is n*3 matrix, n is the number of observations.
int n = x.nrow();
NumericVector density(n);
for (int i = 0; i < n; ++i){
density(i) = dtpoi0(x.row(i),theta)["fvalue"];
}
return(density);
}
// [[Rcpp::export]]
double logtpoi0(const IntegerMatrix& x,const NumericVector theta){
// theta must be a 6-dimiension parameter.
double nln = -sum(log( dtpoi(x,theta) + 1e-60 ));
if(arma::is_finite(nln)) {nln = nln;} else {nln = -1e10;}
return(nln);
}
Huge caveat ahead: I don’t really know Armadillo. But I’ve had a stab at it because the code looks interesting.
A few general things:
You don’t need to declare things before you assign them for the first time. In particular, it’s generally not necessary to declare vectors outside a loop if they’re only used inside the loop. This is probably no less efficient than declaring them inside the loop. However, if your code is too slow it makes sense to carefully profile this, and test whether the assumption holds.
Many of your declarations are just aliases for vector elements and don’t seem necessary.
Your z{1…3} vectors aren’t necessary. C++ has a min function to find the minimum of two elements.
dtpoi0 contains two main loops. Both of these have been heavily modified in my code:
The first loop iterates over many ks that can are never used, due to the internal if that tests whether i + j exceeds s2. By pulling this check into the loop condition of j, we perform fewer k loops.
Your if uses & instead of &&. Like in R, using && rather than & causes short-circuiting. While this is probably not more efficient in this case, using && is idiomatic, whereas & causes head-scratching (my code uses and which is an alternative way of spelling && in C++; I prefer its readability).
The second loops effectively performs a matrix operation manually. I feel that there should be a way of expressing this purely with matrix operations — but as mentioned I’m not an Armadillo user. Still, my changes attempt to vectorise as much of this operation as possible (if nothing else this makes the code shorter). The dpois inner product is unfortunately still inside a loop.
The logic of logtpoi0 can be made more idiomatic and (IMHO) more readable by using the conditional operator instead of if.
const-correctness is a big deal in C++, since it weeds out accidental modifications. Use const liberally when declaring variables that are not supposed to change.
In terms of efficiency, the biggest hit when calling dtpoi or logtpoi0 is probably the conversion of missy to misy, which causes allocations and memory copies. Only convert to IntegerMatrix when necessary, i.e. when actually returning that value to R. For that reason, I’ve split dtpoi0 into two parts.
Another inefficiency is the fact that the first loop in dtpoi0 grows a matrix by appending columns. That’s a big no-no. However, rewriting the code to avoid this isn’t trivial.
#include <algorithm>
#include <RcppArmadillo.h>
// [[Rcpp::depends("RcppArmadillo")]]
using namespace Rcpp;
using namespace arma;
imat dtpoi0_mat(const IntegerVector& x) {
const int s1 = std::min(x[0], x[1]);
const int s2 = std::min(x[0], x[2]);
const int s3 = std::min(x[1], x[2]);
imat missy(1, 3, fill::zeros);
for (int i = 0; i <= s1; ++i) {
for (int j = 0; j <= s2 and i + j <= s1; ++j) {
for (int k = 0; k <= s3 and i + k <= s2 and j + k <= s3; ++k) {
missy = join_cols(missy, irowvec{i, j, k});
}
}
}
return missy;
}
double dtpoi0_fvalue(const IntegerVector& x, const NumericVector& theta, imat& missy) {
double fvalue = 0.0;
ivec xx = as<ivec>(x);
missy.each_row([&](irowvec& v) {
const ivec u(join_cols(xx - v(uvec{0, 0, 1}) - v(uvec{1, 2, 3}), v));
double prod = 1;
for (int i = 0; i < u.n_elem; ++i) {
prod *= R::dpois(u[i], theta[i], 0);
}
fvalue += prod;
});
return fvalue;
}
double dtpoi0_fvalue(const IntegerVector& x, const NumericVector& theta) {
imat missy = dtpoi0_mat(x);
return dtpoi0_fvalue(x, theta, missy);
}
// [[Rcpp::export]]
List dtpoi0(const IntegerVector& x, const NumericVector& theta) {
imat missy = dtpoi0_mat(x);
const double fvalue = dtpoi0_fvalue(x, theta, missy);
return List::create(Named("misy") = as<IntegerMatrix>(wrap(missy)), Named("fvalue") = fvalue);
}
// [[Rcpp::export]]
NumericVector dtpoi(const IntegerMatrix& x, const NumericVector& theta) {
//x is n*3 matrix, n is the number of observations.
int n = x.nrow();
NumericVector density(n);
for (int i = 0; i < n; ++i){
density(i) = dtpoi0_fvalue(x.row(i), theta);
}
return density;
}
// [[Rcpp::export]]
double logtpoi0(const IntegerMatrix& x, const NumericVector theta) {
// theta must be a 6-dimension parameter.
const double nln = -sum(log(dtpoi(x, theta) + 1e-60));
return is_finite(nln) ? nln : -1e10;
}
Important: This compiles, but I can’t test its correctness. It’s entirely possible (even likely!) that my refactor introduced errors. It should therefore only be viewed as a solution sketch, and should by no means be copied and pasted into an application.

How does the OpenCV code for the Mixture of Gaussian Algorithm determine a pixel is foreground?

I am trying to study the source code of the Mixture of Gaussian algorithm implemented in OpenCV 3.0 (the source code is in the contrib repository). I am focusing on the following code segment (retrieved from opencv_contrib-master\modules\bgsegm\src\bgfg_gaussmix.cpp, using the single-channel version process8uC1() as example):
for( y = 0; y < rows; y++ )
{
const uchar* src = image.ptr<uchar>(y);
uchar* dst = fgmask.ptr<uchar>(y);
// ... removed some code as I only consider learning rate within [0, 1]
for( x = 0; x < cols; x++, mptr += K )
{
float wsum = 0;
float pix = src[x];
int kHit = -1, kForeground = -1;
for( k = 0; k < K; k++ )
{
float w = mptr[k].weight;
wsum += w;
if( w < FLT_EPSILON )
break;
float mu = mptr[k].mean;
float var = mptr[k].var;
float diff = pix - mu;
float d2 = diff*diff;
if( d2 < vT*var )
{
wsum -= w;
float dw = alpha*(1.f - w);
mptr[k].weight = w + dw;
mptr[k].mean = mu + alpha*diff;
var = std::max(var + alpha*(d2 - var), minVar);
mptr[k].var = var;
mptr[k].sortKey = w/std::sqrt(var);
for( k1 = k-1; k1 >= 0; k1-- )
{
if( mptr[k1].sortKey >= mptr[k1+1].sortKey )
break;
std::swap( mptr[k1], mptr[k1+1] );
}
kHit = k1+1;
break;
}
}
if( kHit < 0 ) // no appropriate gaussian mixture found at all, remove the weakest mixture and create a new one
{
kHit = k = std::min(k, K-1);
wsum += w0 - mptr[k].weight;
mptr[k].weight = w0;
mptr[k].mean = pix;
mptr[k].var = var0;
mptr[k].sortKey = sk0;
}
else
for( ; k < K; k++ )
wsum += mptr[k].weight;
float wscale = 1.f/wsum;
wsum = 0;
for( k = 0; k < K; k++ )
{
wsum += mptr[k].weight *= wscale;
mptr[k].sortKey *= wscale;
if( wsum > T && kForeground < 0 )
kForeground = k+1;
}
dst[x] = (uchar)(-(kHit >= kForeground));
}
}
As far as I understand, the pixel is considered as foreground if kHit >= kForeground at the end of the code segment. And this may occur in 2 cases:
when kHit == -1 after the first for(k) loop. This is easy to interpret, as none of the Gaussian curves can fit the current pixel, i.e. all Gaussian curves treat the current pixel as foreground. Or
when kHit > -1 after the first for(k) loop, and kHit >= kForeground, where kForeground is found in the final for(k) loop.
What I do not understand are:
What is the physical interpretation of the condition if( wsum > T && kForeground < 0 )? I know that wsum is the running cumulative sum of the weights of the first k curves, and T is the background ratio set by user between [0, 1], but why do we need to calculate the cumulative sum of the weights and compare with the background ratio?
why do we need to sort the curves according to the descending order of sortKey, which is calculated as w/std::sqrt(var) (weight / standard deviation)? Why not simply sort according to the descending order of weight of the curves?

Polynomial multiplication in M2(R)?

I was trying to implement a FFT-based multiplication algorithm in M2(R). Basically an algorithm that gets as an input two polynoms with elements given as matrices, and builds the product polynom. However, even though the algorithm should work, as it looks exactly identical to a version I wrote earlier on regular number, it doesn't. The coefficients are always off by a little.
I have not found any articles on the roots of unity in M2(C), but I have found(on paper) that choosing eps = ((cos(2PI/n) , i sin(2PI/n)) , ( i sin(2PI/n) , cos(2PI/n))), I get a nice cycle.
Is there something wrong in my approach?
Here is the code:
struct FFT {
PolyC To, Aux[17][2], Res[17][2], AC, BC, ResC, ResD, ArgA, ArgB;
void fft(PolyC V, var depth, var n, PolyC To, MatC step) {
if(n == 1) {
To[0] = V[0];
} else {
MatC eps = matCHelper.I2;
//We "split" the poly in 2
for(var i=0; i<n; i++)
Aux[depth+1][i&1][i>>1] = V[i];
//We recursively apply FFT to the components
fft(Aux[depth+1][0], depth+1, n/2, Res[depth+1][0], step*step);
fft(Aux[depth+1][1], depth+1, n/2, Res[depth+1][1], step*step);
//We compute the result for the n roots
for(var i=0; i<n/2; i++) {
To[i] = Res[depth+1][0][i] + eps * Res[depth+1][1][i];
To[n/2+i] = Res[depth+1][0][i] - eps * Res[depth+1][1][i];
eps = eps * step;
}
}
}
void FFTMultiply(Poly Res, Poly A, Poly B, var n1, var n2) {
var M;
for(M = 1; M <= 2*n1 || M <= 2*n2; M <<= 1);
for(var i=0; i<n1; i++) ArgA[i] = A[i];
for(var i=n1; i<M; i++) ArgA[i] = matCHelper.O2;
for(var i=0; i<n2; i++) ArgB[i] = B[i];
for(var i=n2; i<M; i++) ArgB[i] = matCHelper.O2;
MatC step( Complex(cos(2*PI/M), 0) , Complex(0, sin(2*PI/M)),
Complex(0, sin(2*PI/M)) , Complex(cos(2*PI/M), 0) );
fft(ArgA, 0, M, AC, step);
fft(ArgB, 0, M, BC, step);
for(var i=0; i<M; i++) {
RezC[i] = AC[i] * BC[i];
}
step.b = -step.b;
step.c = -step.c;
fft(RezC, 0, M, RezD, step);
for(var i=0; i<M; i++) {
// Now I divided everything by M and copied every element of ResD to Res modulo some number
}
}
};
You can not expect this method to work if your coefficient matrices do not commute with your matrix step. To get it working correctly, use the diagonal matrix corresponding to multiplication with the scalar exp(i*2*PI/M).

Obtaining orientation map of fingerprint image using OpenCV

I'm trying to implement the method of improving fingerprint images by Anil Jain. As a starter, I encountered some difficulties while extracting the orientation image, and am strictly following those steps described in Section 2.4 of that paper.
So, this is the input image:
And this is after normalization using exactly the same method as in that paper:
I'm expecting to see something like this (an example from the internet):
However, this is what I got for displaying obtained orientation matrix:
Obviously this is wrong, and it also gives non-zero values for those zero points in the original input image.
This is the code I wrote:
cv::Mat orientation(cv::Mat inputImage)
{
cv::Mat orientationMat = cv::Mat::zeros(inputImage.size(), CV_8UC1);
// compute gradients at each pixel
cv::Mat grad_x, grad_y;
cv::Sobel(inputImage, grad_x, CV_16SC1, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
cv::Sobel(inputImage, grad_y, CV_16SC1, 0, 1, 3, 1, 0, cv::BORDER_DEFAULT);
cv::Mat Vx, Vy, theta, lowPassX, lowPassY;
cv::Mat lowPassX2, lowPassY2;
Vx = cv::Mat::zeros(inputImage.size(), inputImage.type());
Vx.copyTo(Vy);
Vx.copyTo(theta);
Vx.copyTo(lowPassX);
Vx.copyTo(lowPassY);
Vx.copyTo(lowPassX2);
Vx.copyTo(lowPassY2);
// estimate the local orientation of each block
int blockSize = 16;
for(int i = blockSize/2; i < inputImage.rows - blockSize/2; i+=blockSize)
{
for(int j = blockSize / 2; j < inputImage.cols - blockSize/2; j+= blockSize)
{
float sum1 = 0.0;
float sum2 = 0.0;
for ( int u = i - blockSize/2; u < i + blockSize/2; u++)
{
for( int v = j - blockSize/2; v < j+blockSize/2; v++)
{
sum1 += grad_x.at<float>(u,v) * grad_y.at<float>(u,v);
sum2 += (grad_x.at<float>(u,v)*grad_x.at<float>(u,v)) * (grad_y.at<float>(u,v)*grad_y.at<float>(u,v));
}
}
Vx.at<float>(i,j) = sum1;
Vy.at<float>(i,j) = sum2;
double calc = 0.0;
if(sum1 != 0 && sum2 != 0)
{
calc = 0.5 * atan(Vy.at<float>(i,j) / Vx.at<float>(i,j));
}
theta.at<float>(i,j) = calc;
// Perform low-pass filtering
float angle = 2 * calc;
lowPassX.at<float>(i,j) = cos(angle * pi / 180);
lowPassY.at<float>(i,j) = sin(angle * pi / 180);
float sum3 = 0.0;
float sum4 = 0.0;
for(int u = -lowPassSize / 2; u < lowPassSize / 2; u++)
{
for(int v = -lowPassSize / 2; v < lowPassSize / 2; v++)
{
sum3 += inputImage.at<float>(u,v) * lowPassX.at<float>(i - u*lowPassSize, j - v * lowPassSize);
sum4 += inputImage.at<float>(u, v) * lowPassY.at<float>(i - u*lowPassSize, j - v * lowPassSize);
}
}
lowPassX2.at<float>(i,j) = sum3;
lowPassY2.at<float>(i,j) = sum4;
float calc2 = 0.0;
if(sum3 != 0 && sum4 != 0)
{
calc2 = 0.5 * atan(lowPassY2.at<float>(i, j) / lowPassX2.at<float>(i, j)) * 180 / pi;
}
orientationMat.at<float>(i,j) = calc2;
}
}
return orientationMat;
}
I've already searched a lot on the web, but almost all of them are in Matlab. And there exist very few ones using OpenCV, but they didn't help me either. I sincerely hope someone could go through my code and point out any error to help. Thank you in advance.
Update
Here are the steps that I followed according to the paper:
Obtain normalized image G.
Divide G into blocks of size wxw (16x16).
Compute the x and y gradients at each pixel (i,j).
Estimate the local orientation of each block centered at pixel (i,j) using equations:
Perform low-pass filtering to remove noise. For that, convert the orientation image into a continuous vector field defined as:
where W is a two-dimensional low-pass filter, and w(phi) x w(phi) is its size, which equals to 5.
Finally, compute the local ridge orientation at (i,j) using:
Update2
This is the output of orientationMat after changing the mat type to CV_16SC1 in Sobel operation as Micka suggested:
Maybe it's too late for me to answer, but anyway somebody could read this later and solve the same problem.
I've been working for a while in the same algorithm, same method you posted... But there's some writting errors when the papper was redacted (I guess). After fighting a lot with the equations I found this errors by looking other similar works.
Here is what worked for me...
Vy(i, j) = 2*dx(u,v)*dy(u,v)
Vx(i,j) = dx(u,v)^2 - dy(u,v)^2
O(i,j) = 0.5*arctan(Vy(i,j)/Vx(i,j)
(Excuse me I wasn't able to post images, so I wrote the modified ecuations. Remeber "u" and "v" are positions of the summation across the BlockSize by BlockSize window)
The first thing and most important (obviously) are the equations, I saw that in different works this expressions were really different and in every one they talked about the same algorithm of Hong et al.
The Key is finding the Least Mean Square (First 3 equations) of the gradients (Vx and Vy), I provided the corrected formulas above for this ation. Then you can compute angle theta for the non overlapping window (16x16 size recommended in the papper), after that the algorithm says you must calculate the magnitud of the doubled angle in "x" and "y" directions (Phi_x and Phi_y).
Phi_x(i,j) = V(i,j) * cos(2*O(i,j))
Phi_y(i,j) = V(i,j) * sin(2*O(i,j))
Magnitud is just:
V = sqrt(Vx(i,j)^2 + Vy(i,j)^2)
Note that in the related work doesn't mention that you have to use the gradient magnitud, but it make sense (for me) in doing it. After all this corrections you can apply the low pass filter to Phi_x and Phi_y, I used a simple Mask of size 5x5 to average this magnitudes (something like medianblur() of opencv).
Last thing is to calculate new angle, that is the average of the 25ith neighbors in the O(i,j) image, for this you just have to:
O'(i,j) = 0.5*arctan(Phi_y/Phi_x)
We're just there... All this just for calculating the angle of the NORMAL VECTOR TO THE RIDGES DIRECTIONS (O'(i,j)) in the BlockSize by BlockSize non overlapping window, what does it mean? it means that the angle we just calculated is perpendicular to the ridges, in simple words we just calculated the angle of the riges plus 90 degrees... To get the angle we need, we just have to substract to the obtained angle 90°.
To draw the lines we need to have an initial point (X0, Y0) and a final point(X1, Y1). For that imagine a circle centered on (X0, Y0) with a radious of "r":
x0 = i + blocksize/2
y0 = j + blocksize/2
r = blocksize/2
Note we add i and j to the first coordinates becouse the window is moving and we are gonna draw the line starting from the center of the non overlaping window, so we can't use just the center of the non overlaping window.
Then to calculate the end coordinates to draw a line we can just have to use a right triangle so...
X1 = r*cos(O'(i,j)-90°)+X0
Y1 = r*sin(O'(i,j)-90°)+Y0
X2 = X0-r*cos(O'(i,j)-90°)
Y2 = Y0-r*cos(O'(i,j)-90°)
Then just use opencv line function, where initial Point is (X0,Y0) and final Point is (X1, Y1). Additional to it, I drawed the windows of 16x16 and computed the oposite points of X1 and Y1 (X2 and Y2) to draw a line of the entire window.
Hope this help somebody.
My results...
Main function:
Mat mat = imread("nwmPa.png",0);
mat.convertTo(mat, CV_32F, 1.0/255, 0);
Normalize(mat);
int blockSize = 6;
int height = mat.rows;
int width = mat.cols;
Mat orientationMap;
orientation(mat, orientationMap, blockSize);
Normalize:
void Normalize(Mat & image)
{
Scalar mean, dev;
meanStdDev(image, mean, dev);
double M = mean.val[0];
double D = dev.val[0];
for(int i(0) ; i<image.rows ; i++)
{
for(int j(0) ; j<image.cols ; j++)
{
if(image.at<float>(i,j) > M)
image.at<float>(i,j) = 100.0/255 + sqrt( 100.0/255*pow(image.at<float>(i,j)-M,2)/D );
else
image.at<float>(i,j) = 100.0/255 - sqrt( 100.0/255*pow(image.at<float>(i,j)-M,2)/D );
}
}
}
Orientation map:
void orientation(const Mat &inputImage, Mat &orientationMap, int blockSize)
{
Mat fprintWithDirectionsSmoo = inputImage.clone();
Mat tmp(inputImage.size(), inputImage.type());
Mat coherence(inputImage.size(), inputImage.type());
orientationMap = tmp.clone();
//Gradiants x and y
Mat grad_x, grad_y;
// Sobel(inputImage, grad_x, CV_32F, 1, 0, 3, 1, 0, BORDER_DEFAULT);
// Sobel(inputImage, grad_y, CV_32F, 0, 1, 3, 1, 0, BORDER_DEFAULT);
Scharr(inputImage, grad_x, CV_32F, 1, 0, 1, 0);
Scharr(inputImage, grad_y, CV_32F, 0, 1, 1, 0);
//Vector vield
Mat Fx(inputImage.size(), inputImage.type()),
Fy(inputImage.size(), inputImage.type()),
Fx_gauss,
Fy_gauss;
Mat smoothed(inputImage.size(), inputImage.type());
// Local orientation for each block
int width = inputImage.cols;
int height = inputImage.rows;
int blockH;
int blockW;
//select block
for(int i = 0; i < height; i+=blockSize)
{
for(int j = 0; j < width; j+=blockSize)
{
float Gsx = 0.0;
float Gsy = 0.0;
float Gxx = 0.0;
float Gyy = 0.0;
//for check bounds of img
blockH = ((height-i)<blockSize)?(height-i):blockSize;
blockW = ((width-j)<blockSize)?(width-j):blockSize;
//average at block WхW
for ( int u = i ; u < i + blockH; u++)
{
for( int v = j ; v < j + blockW ; v++)
{
Gsx += (grad_x.at<float>(u,v)*grad_x.at<float>(u,v)) - (grad_y.at<float>(u,v)*grad_y.at<float>(u,v));
Gsy += 2*grad_x.at<float>(u,v) * grad_y.at<float>(u,v);
Gxx += grad_x.at<float>(u,v)*grad_x.at<float>(u,v);
Gyy += grad_y.at<float>(u,v)*grad_y.at<float>(u,v);
}
}
float coh = sqrt(pow(Gsx,2) + pow(Gsy,2)) / (Gxx + Gyy);
//smoothed
float fi = 0.5*fastAtan2(Gsy, Gsx)*CV_PI/180;
Fx.at<float>(i,j) = cos(2*fi);
Fy.at<float>(i,j) = sin(2*fi);
//fill blocks
for ( int u = i ; u < i + blockH; u++)
{
for( int v = j ; v < j + blockW ; v++)
{
orientationMap.at<float>(u,v) = fi;
Fx.at<float>(u,v) = Fx.at<float>(i,j);
Fy.at<float>(u,v) = Fy.at<float>(i,j);
coherence.at<float>(u,v) = (coh<0.85)?1:0;
}
}
}
} ///for
GaussConvolveWithStep(Fx, Fx_gauss, 5, blockSize);
GaussConvolveWithStep(Fy, Fy_gauss, 5, blockSize);
for(int m = 0; m < height; m++)
{
for(int n = 0; n < width; n++)
{
smoothed.at<float>(m,n) = 0.5*fastAtan2(Fy_gauss.at<float>(m,n), Fx_gauss.at<float>(m,n))*CV_PI/180;
if((m%blockSize)==0 && (n%blockSize)==0){
int x = n;
int y = m;
int ln = sqrt(2*pow(blockSize,2))/2;
float dx = ln*cos( smoothed.at<float>(m,n) - CV_PI/2);
float dy = ln*sin( smoothed.at<float>(m,n) - CV_PI/2);
arrowedLine(fprintWithDirectionsSmoo, Point(x, y+blockH), Point(x + dx, y + blockW + dy), Scalar::all(255), 1, CV_AA, 0, 0.06*blockSize);
// qDebug () << Fx_gauss.at<float>(m,n) << Fy_gauss.at<float>(m,n) << smoothed.at<float>(m,n);
// imshow("Orientation", fprintWithDirectionsSmoo);
// waitKey(0);
}
}
}///for2
normalize(orientationMap, orientationMap,0,1,NORM_MINMAX);
imshow("Orientation field", orientationMap);
orientationMap = smoothed.clone();
normalize(smoothed, smoothed, 0, 1, NORM_MINMAX);
imshow("Smoothed orientation field", smoothed);
imshow("Coherence", coherence);
imshow("Orientation", fprintWithDirectionsSmoo);
}
seems nothing forgot )
I have read your code thoroughly and found that you have made a mistake while calculating sum3 and sum4:
sum3 += inputImage.at<float>(u,v) * lowPassX.at<float>(i - u*lowPassSize, j - v * lowPassSize);
sum4 += inputImage.at<float>(u, v) * lowPassY.at<float>(i - u*lowPassSize, j - v * lowPassSize);
instead of inputImage you should use a low pass filter.

Finding a Tangent Line at a Point on a Randomized Slope

I have a piece of processing code that I was given, which appears to be setting up a randomized Fourier series. Unfortunately, despite my efforts to improve my mathematical skills, I have no idea what it is doing and the articles I have found are not much help.
I'm trying to extend this code so that I can draw a line tangent to a point on the slope created by the code bellow. The closest I can find to answering this is in the mathematics forum. Unfortunately, I don't really understand what is being discussed or if it really is relevant to my situation.
Any assistance on how I would go about calculating a tangent line at a particular point on this curve would be much appreciated.
UPDATE As of 06/17/13
I've been trying to play around with this, but without much success. This is the best I can do, and I doubt that I'm applying the derivative correctly to find the tangent (or even if I have found the derivative at the point correctly). Also, I'm beginning to worry that I'm not drawing the line correctly even if I have everything else correct. If anyone can provide input on this I'd appreciate it.
final int w = 800;
final int h = 480;
double[] skyline;
PImage img;
int numOfDeriv = 800;
int derivModBy = 1; //Determines how many points will be checked
int time;
int timeDelay = 1000;
int iter;
double[] derivatives;
void setup() {
noStroke();
size(w, h);
fill(0,128,255);
rect(0,0,w,h);
int t[] = terrain(w,h);
fill(77,0,0);
for(int i=0; i < w; i++){
rect(i, h, 1, -1*t[i]);
}
time = millis();
timeDelay = 100;
iter =0;
img = get();
}
void draw() {
int dnum = 0; //Current position of derivatives
if(iter == numOfDeriv) iter = 0;
if (millis() > time + timeDelay){
image(img, 0, 0, width, height);
strokeWeight(4);
stroke(255,0,0);
point((float)iter*derivModBy, height-(float)skyline[iter*derivModBy]);
strokeWeight(1);
stroke(255,255,0);
print("At x = ");
print(iter);
print(", y = ");
print(skyline[iter]);
print(", derivative = ");
print((float)derivatives[iter]);
print('\n');
lineAngle(iter, (int)(height-skyline[iter]), (float)derivatives[iter], 100);
lineAngle(iter, (int)(height-skyline[iter]), (float)derivatives[iter], -100);
stroke(126);
time = millis();
iter += 1;
}
}
void lineAngle(int x, int y, float angle, float length)
{
line(x, y, x+cos(angle)*length, y-sin(angle)*length);
}
int[] terrain(int w, int h){
width = w;
height = h;
//min and max bracket the freq's of the sin/cos series
//The higher the max the hillier the environment
int min = 1, max = 6;
//allocating horizon for screen width
int[] horizon = new int[width];
skyline = new double[width];
derivatives = new double[numOfDeriv];
//ratio of amplitude of screen height to landscape variation
double r = (int) 2.0/5.0;
//number of terms to be used in sine/cosine series
int n = 4;
int[] f = new int[n*2];
//calculating omegas for sine series
for(int i = 0; i < n*2 ; i ++){
f[i] = (int) random(max - min + 1) + min;
}
//amp is the amplitude of the series
int amp = (int) (r*height);
int dnum = 0; //Current number of derivatives
for(int i = 0 ; i < width; i ++){
skyline[i] = 0;
double derivative = 0.0;
for(int j = 0; j < n; j++){
if(i % derivModBy == 0){
derivative += ( cos( (f[j]*PI*i/height) * f[j]*PI/height) -
sin(f[j+n]*PI*i/height) * f[j+n]*PI/height);
}
skyline[i] += ( sin( (f[j]*PI*i/height) ) + cos(f[j+n]*PI*i/height) );
}
skyline[i] *= amp/(n*2);
skyline[i] += (height/2);
skyline[i] = (int)skyline[i];
horizon[i] = (int)skyline[i];
derivative *= amp/(n*2);
if(i % derivModBy == 0){
derivatives[dnum++] = derivative;
derivative = 0;
}
}
return horizon;
}
void reset() {
time = millis();
}
Well it seems in this particular case that you don't need to understand much about the Fourier Series, just that it has the form:
A0 + A1*cos(x) + A2*cos(2*x) + A3*cos(3*x) +... + B1*sin(x) + B2*sin(x) +...
Normally you're given a function f(x) and you need to find the values of An and Bn such that the Fourier series converges to your function (as you add more terms) for some interval [a, b].
In this case however they want a random function that just looks like different lumps and pits (or hills and valleys as the context might suggest) so they choose random terms from the Fourier Series between min and max and set their coefficients to 1 (and conceptually 0 otherwise). They also satisfy themselves with a Fourier series of 4 sine terms and 4 cosine terms (which is certainly easier to manage than an infinite number of terms). This means that their Fourier Series ends up looking like different sine and cosine functions of different frequencies added together (and all have the same amplitude).
Finding the derivative of this is easy if you recall that:
sin(n*x)' = n * cos(x)
cos(n*x)' = -n * sin(x)
(f(x) + g(x))' = f'(x) + g'(x)
So the loop to calculate the the derivative would look like:
for(int j = 0; j < n; j++){
derivative += ( cos( (f[j]*PI*i/height) * f[j]*PI/height) - \
sin(f[j+n]*PI*i/height) * f[j+n]*PI/height);
}
At some point i (Note the derivative is being taken with respect to i since that is the variable that represents our x position here).
Hopefully with this you should be able to calculate the equation of the tangent line at a point i.
UPDATE
At the point where you do skyline[i] *= amp/(n*2); you must also adjust your derivative accordingly derivative *= amp/(n*2); however your derivative does not need adjusting when you do skyline[i] += height/2;
I received an answer to this problem via "quarks" on processing.org form. Essentially the problem is that I was taking the derivative of each term of the series instead of taking the derivative of the sum of the entire series. Also, I wasn't applying my result correctly anyway.
Here is the code that quarks provided that definitively solves this problem.
final int w = 800;
final int h = 480;
float[] skyline;
PImage img;
int numOfDeriv = 800;
int derivModBy = 1; //Determines how many points will be checked
int time;
int timeDelay = 1000;
int iter;
float[] tangents;
public void setup() {
noStroke();
size(w, h);
fill(0, 128, 255);
rect(0, 0, w, h);
terrain(w, h);
fill(77, 0, 0);
for (int i=0; i < w; i++) {
rect(i, h, 1, -1*(int)skyline[i]);
}
time = millis();
timeDelay = 100;
iter =0;
img = get();
}
public void draw() {
if (iter == numOfDeriv) iter = 0;
if (millis() > time + timeDelay) {
image(img, 0, 0, width, height);
strokeWeight(4);
stroke(255, 0, 0);
point((float)iter*derivModBy, height-(float)skyline[iter*derivModBy]);
strokeWeight(1);
stroke(255, 255, 0);
print("At x = ");
print(iter);
print(", y = ");
print(skyline[iter]);
print(", derivative = ");
print((float)tangents[iter]);
print('\n');
lineAngle(iter, (int)(height-skyline[iter]), (float)tangents[iter], 100);
lineAngle(iter, (int)(height-skyline[iter]), (float)tangents[iter], -100);
stroke(126);
time = millis();
iter += 1;
}
}
public void lineAngle(int x, int y, float angle, float length) {
line(x, y, x+cos(angle)*length, y-sin(angle)*length);
}
public void terrain(int w, int h) {
//min and max bracket the freq's of the sin/cos series
//The higher the max the hillier the environment
int min = 1, max = 6;
skyline = new float[w];
tangents = new float[w];
//ratio of amplitude of screen height to landscape variation
double r = (int) 2.0/5.0;
//number of terms to be used in sine/cosine series
int n = 4;
int[] f = new int[n*2];
//calculating omegas for sine series
for (int i = 0; i < n*2 ; i ++) {
f[i] = (int) random(max - min + 1) + min;
}
//amp is the amplitude of the series
int amp = (int) (r*h);
for (int i = 0 ; i < w; i ++) {
skyline[i] = 0;
for (int j = 0; j < n; j++) {
skyline[i] += ( sin( (f[j]*PI*i/h) ) + cos(f[j+n]*PI*i/h) );
}
skyline[i] *= amp/(n*2);
skyline[i] += (h/2);
}
for (int i = 1 ; i < w - 1; i ++) {
tangents[i] = atan2(skyline[i+1] - skyline[i-1], 2);
}
tangents[0] = atan2(skyline[1] - skyline[0], 1);
tangents[w-1] = atan2(skyline[w-2] - skyline[w-1], 1);
}
void reset() {
time = millis();
}

Resources