/** Kullback-Leibner divergence */ double getKullbackLeibnerDivergence(MultidimArray<Complex >& Fimg, MultidimArray<Complex >& Fref, MultidimArray<double>& sigma2, MultidimArray<double>& p_i, MultidimArray<double>& q_i, int highshell, int lowshell) { // First check dimensions are OK if (!Fimg.sameShape(Fref)) { REPORT_ERROR("getKullbackLeibnerDivergence ERROR: Fimg and Fref are not of the same shape."); } if (highshell < 0) { highshell = XSIZE(Fimg) - 1; } if (lowshell < 0) { lowshell = 0; } if (highshell > XSIZE(sigma2)) { REPORT_ERROR("getKullbackLeibnerDivergence ERROR: highshell is larger than size of sigma2 array."); } if (highshell < lowshell) { REPORT_ERROR("getKullbackLeibnerDivergence ERROR: highshell is smaller than lowshell."); } // Initialize the histogram MultidimArray<int> histogram; int histogram_size = 101; int histogram_origin = histogram_size / 2; double sigma_max = 10.; double histogram_factor = histogram_origin / sigma_max; histogram.initZeros(histogram_size); // This way this will work in both 2D and 3D FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fimg) { int ires = ROUND(sqrt(kp * kp + ip * ip + jp * jp)); if (ires >= lowshell && ires <= highshell) { // Use FT of masked image for noise estimation! double diff_real = (DIRECT_A3D_ELEM(Fref, k, i, j)).real - (DIRECT_A3D_ELEM(Fimg, k, i, j)).real; double diff_imag = (DIRECT_A3D_ELEM(Fref, k, i, j)).imag - (DIRECT_A3D_ELEM(Fimg, k, i, j)).imag; double sigma = sqrt(DIRECT_A1D_ELEM(sigma2, ires)); // Divide by standard deviation to normalise all the difference diff_real /= sigma; diff_imag /= sigma; // Histogram runs from -10 sigma to +10 sigma diff_real += sigma_max; diff_imag += sigma_max; // Make histogram on-the-fly; // Real part int ihis = ROUND(diff_real * histogram_factor); if (ihis < 0) { ihis = 0; } else if (ihis >= histogram_size) { ihis = histogram_size - 1; } histogram(ihis)++; // Imaginary part ihis = ROUND(diff_imag * histogram_factor); if (ihis < 0) { ihis = 0; } else if (ihis > histogram_size) { ihis = histogram_size; } histogram(ihis)++; } } // Normalise the histogram and the discretised analytical Gaussian double norm = (double)histogram.sum(); double gaussnorm = 0.; for (int i = 0; i < histogram_size; i++) { double x = (double)i / histogram_factor; gaussnorm += gaussian1D(x - sigma_max, 1. , 0.); } // Now calculate the actual Kullback-Leibner divergence double kl_divergence = 0.; p_i.resize(histogram_size); q_i.resize(histogram_size); for (int i = 0; i < histogram_size; i++) { // Data distribution p_i(i) = (double)histogram(i) / norm; // Theoretical distribution double x = (double)i / histogram_factor; q_i(i) = gaussian1D(x - sigma_max, 1. , 0.) / gaussnorm; if (p_i(i) > 0.) { kl_divergence += p_i(i) * log(p_i(i) / q_i(i)); } } kl_divergence /= (double)histogram_size; return kl_divergence; }
void PolyZernikes::fit(const Matrix1D<int> & coef, MultidimArray<double> & im, MultidimArray<double> &weight, MultidimArray<bool> & ROI, int verbose) { this->create(coef); size_t xdim = XSIZE(im); size_t ydim = YSIZE(im); //int numZer = (size_t)coef.sum(); int numZer = (size_t)coef.sum(); //Actually polOrder corresponds to the polynomial order +1 int polOrder=(int)ZERNIKE_ORDER(coef.size()); im.setXmippOrigin(); Matrix2D<double> polValue(polOrder,polOrder); //First argument means number of images //Second argument means number of pixels WeightedLeastSquaresHelper weightedLeastSquaresHelper; Matrix2D<double>& zerMat=weightedLeastSquaresHelper.A; zerMat.resizeNoCopy((size_t)ROI.sum(), numZer); double iMaxDim2 = 2./std::max(xdim,ydim); size_t pixel_idx=0; weightedLeastSquaresHelper.b.resizeNoCopy((size_t)ROI.sum()); weightedLeastSquaresHelper.w.resizeNoCopy(weightedLeastSquaresHelper.b); FOR_ALL_ELEMENTS_IN_ARRAY2D(im) { if ( (A2D_ELEM(ROI,i,j))) { //For one i we swap the different j double y=i*iMaxDim2; double x=j*iMaxDim2; //polValue = [ 0 y y2 y3 ... // x xy xy2 xy3 ... // x2 x2y x2y2 x2y3 ] //dMij(polValue,py,px) py es fila, px es columna for (int py = 0; py < polOrder; ++py) { double ypy=std::pow(y,py); for (int px = 0; px < polOrder; ++px) dMij(polValue,px,py) = ypy*std::pow(x,px); } Matrix2D<int> *fMat; //We generate the representation of the Zernike polynomials for (int k=0; k < numZer; ++k) { fMat = &fMatV[k]; if (fMat == NULL) continue; double temp = 0; for (size_t px = 0; px < (*fMat).Xdim(); ++px) for (size_t py = 0; py < (*fMat).Ydim(); ++py) temp += dMij(*fMat,py,px)*dMij(polValue,py,px); dMij(zerMat,pixel_idx,k) = temp; } VEC_ELEM(weightedLeastSquaresHelper.b,pixel_idx)=A2D_ELEM(im,i,j); VEC_ELEM(weightedLeastSquaresHelper.w,pixel_idx)=std::abs(A2D_ELEM(weight,i,j)); ++pixel_idx; } } Matrix1D<double> zernikeCoefficients; weightedLeastSquares(weightedLeastSquaresHelper, zernikeCoefficients); fittedCoeffs = zernikeCoefficients; // Pointer to the image to be fitted MultidimArray<double> reconstructed; reconstructed.resizeNoCopy(im); pixel_idx=0; FOR_ALL_ELEMENTS_IN_ARRAY2D(im) if (A2D_ELEM(ROI,i,j)) { double temp=0; for (int k=0; k < numZer; ++k) temp+=dMij(zerMat,pixel_idx,k)*VEC_ELEM(fittedCoeffs,k); A2D_ELEM(reconstructed,i,j)=temp; if ( fabs(A2D_ELEM(reconstructed,i,j)-A2D_ELEM(im,i,j)) > PI) A2D_ELEM(ROI,i,j) = false; ++pixel_idx; } pixel_idx=0; if (verbose > 0) { Image<double> save; save()=reconstructed; save.write("reconstructedZernikes.xmp"); ROI.write("ROI.txt"); } }