Xmipp  v3.23.11-Nereus
volume_segment.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: Carlos Oscar coss@cnb.csic.es (2002)
4  *
5  * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  * 02111-1307 USA
21  *
22  * All comments concerning this program package may be sent to the
23  * e-mail address 'xmipp@cnb.csic.es'
24  ***************************************************************************/
25 
26 #include <core/args.h>
27 #include <data/morphology.h>
28 #include <data/filters.h>
29 #include "volume_segment.h"
30 
31 // Read arguments ==========================================================
33 {
35  sampling_rate=-1;
36  threshold=-1;
37  wang_radius=-1;
38  en_threshold=false;
39  do_prob=false;
40  otsu=false;
41 
42  fn_vol = getParam("-i");
43  fn_mask = getParam("-o");
44  method=getParam("--method");
45  if (method=="voxel_mass")
46  voxel_mass = getDoubleParam("--method", 1);
47  else if (method=="dalton_mass")
48  {
49  dalton_mass = getDoubleParam("--method", 1);
50  sampling_rate = getDoubleParam("--method", 2);
51  }
52  else if (method=="aa_mass")
53  {
54  aa_mass = getDoubleParam("--method", 1);
55  sampling_rate = getDoubleParam("--method", 2);
56  }
57  else if (method=="threshold")
58  {
59  en_threshold = true;
60  threshold = getDoubleParam("--method", 1);
61  }
62  else if (method=="otsu")
63  otsu=true;
64  else if (method=="prob")
65  {
66  do_prob = true;
67  wang_radius = getIntParam("--method", 1);
68  }
69 }
70 
71 // Show ====================================================================
73 {
74  std::cout
75  << "Input file : " << fn_vol << std::endl
76  << "Voxel mass : " << voxel_mass << std::endl
77  << "Dalton mass : " << dalton_mass << std::endl
78  << "AA mass : " << aa_mass << std::endl
79  << "Sampling rate: " << sampling_rate << std::endl
80  << "Output mask : " << fn_mask << std::endl
81  << "Enable thres.: " << en_threshold << std::endl
82  << "Threshold : " << threshold << std::endl
83  << "Otsu : " << otsu << std::endl
84  << "Wang radius : " << wang_radius << std::endl
85  << "Probabilistic: " << do_prob << std::endl
86  ;
87 }
88 
89 // usage ===================================================================
91 {
92  addParamsLine(" -i <volume> : Volume to segment");
93  addParamsLine(" [-o <mask=\"\">] : Output mask");
94  addParamsLine(" --method <method> : Segmentation method");
95  addParamsLine(" where <method>");
96  addParamsLine(" voxel_mass <mass> : Mass in voxels");
97  addParamsLine(" dalton_mass <mass> <Tm=1> : Mass in daltons");
98  addParamsLine(" : Tm is the sampling rate (A/pixel)");
99  addParamsLine(" aa_mass <mass> <Tm=1> : Mass in aminoacids");
100  addParamsLine(" : Tm is the sampling rate (A/pixel)");
101  addParamsLine(" threshold <th> : Thresholding");
102  addParamsLine(" otsu : Otsu's method segmentation");
103  addParamsLine(" prob <radius=-1> : Probabilistic solvent mask (typical value 3)");
104  addParamsLine(" : Radius [pix] is used for B.C. Wang cone smoothing");
105 }
106 
107 // Produce side information ================================================
109 {
110  V.read(fn_vol);
111  if (method=="dalton_mass" || method=="aa_mass")
112  {
113  if ((dalton_mass == -1 && aa_mass == -1) || sampling_rate == -1)
114  REPORT_ERROR(ERR_VALUE_INCORRECT, "No way to compute voxel mass");
115  double sampling_rate3 = sampling_rate * sampling_rate * sampling_rate;
116  if (dalton_mass != -1)
117  voxel_mass = dalton_mass * 1.207 / sampling_rate3;
118  else
119  voxel_mass = aa_mass * 110 * 1.207 / sampling_rate3;
120  std::cout << std::endl << "Derived voxel_mass=" << voxel_mass << std::endl;
121  }
122 }
123 
124 // Count voxels ============================================================
125 // Segment with a given threshold and compute the number of voxels in the
126 // biggest piece
127 //#define DEBUG
128 double segment_threshold(const Image<double> *V_in, Image<double> *V_out,
129  double threshold, bool do_prob)
130 {
131  Image<double> aux;
132 
133  // Binarize input volume
134  (*V_out)() = (*V_in)();
135  (*V_out)().threshold("below", threshold, threshold);
136  (*V_out)().binarize(threshold);
137 
138 #ifdef DEBUG
139 
140  std::cout << threshold << std::endl;
141  Image<double> save;
142  save() = (*V_in)();
143  save.write("PPP0.vol");
144  save() = (*V_out)();
145  save.write("PPP1.vol");
146 #endif
147 
148  if (!do_prob)
149  {
150  // Apply morphological opening to input volume
151  aux().initZeros((*V_out)());
152  opening3D((*V_out)(), aux(), 18, 0, 1);
153  closing3D(aux(), (*V_out)(), 18, 0, 1);
154 #ifdef DEBUG
155 
156  save() = (*V_out)();
157  save.write("PPP2.vol");
158 #endif
159  }
160 
161  // Count the number of different objects
162  int no_comp = labelImage3D((*V_out)(), aux());
163  Matrix1D<double> count(no_comp + 1);
164  const MultidimArray<double> &maux=aux();
166  VEC_ELEM(count,(int)A3D_ELEM(maux,k, i, j))++;
167 
168 #ifdef DEBUG
169 
170  std::cout << count << std::endl << std::endl;
171  std::cout << "Press any key\n";
172  char c;
173  std::cin >> c;
174 #endif
175 
176  // Pick the maximum
177  count(0) = 0; // We don't want to pick the background
178  int imax=count.maxIndex();
179 
180  // Select the mask with only that piece
181  const MultidimArray<double> &mVout=(*V_out)();
183  A3D_ELEM(mVout,k, i, j) = A3D_ELEM(maux,k, i, j) == imax;
184 
185  return count(imax);
186 }
187 
188 void wang_smoothing(const Image<double> *V_in, Image<double> *V_out, int radius)
189 {
190 
191  int r2;
192  double radius2 = radius * radius;
193  double sumw, weight;
194 
195  (*V_out)().initZeros((*V_in)());
196 
197  const MultidimArray<double> &mVin=(*V_in)();
198  const MultidimArray<double> &mVout=(*V_out)();
200  {
201  sumw = 0.;
202  for (int kp = k - radius; kp < k + radius; kp++)
203  {
204  if (kp > STARTINGZ(mVin) && kp < FINISHINGZ(mVin))
205  {
206  for (int ip = i - radius; ip < i + radius; ip++)
207  {
208  if (ip > STARTINGY(mVin) && ip < FINISHINGY(mVin))
209  {
210  for (int jp = j - radius; jp < j + radius; jp++)
211  {
212  if (jp > STARTINGX(mVin) && jp < FINISHINGX(mVin))
213  {
214  r2 = (kp - k) * (kp - k) + (ip - i) * (ip - i) + (jp - j) * (jp - j);
215  if ((r2 < radius2) && (A3D_ELEM(mVin, kp, ip, jp) > 0.))
216  {
217  weight = 1. - sqrt(r2 / radius2);
218  A3D_ELEM(mVout, k, i, j) += weight * XMIPP_MAX(0., A3D_ELEM(mVin, kp, ip, jp));
219  sumw += weight;
220  }
221  }
222  }
223  }
224  }
225  }
226  }
227  if (sumw > 0.)
228  A3D_ELEM(mVout, k, i, j) /= sumw;
229  }
230 }
231 
232 
234 {
235  // Calculate mean and sigma for protein and solvent regions
236  // according to the traditional segmentation
237  double Np, sump, sum2p, Ns, sums, sum2s;
238  double avgp, sigp, avgs, sigs, aux, solv_frac, prot_frac;
239  double p_prot, p_solv;
240 
241  MultidimArray<double> &mVin=(*V_in)();
242  MultidimArray<double> &mVout=(*V_out)();
243  mVin.setXmippOrigin();
244  mVout.setXmippOrigin();
245 
246  Np = sump = sum2p = Ns = sums = sum2s = 0.;
248  {
249  aux = A3D_ELEM(mVin, k, i, j);
250  if (A3D_ELEM(mVout, k, i, j) < 0.5)
251  {
252  sums += aux;
253  sum2s += aux * aux;
254  Ns += 1.;
255  }
256  else
257  {
258  sump += aux;
259  sum2p += aux * aux;
260  Np += 1.;
261  }
262  }
263  if (Np > 0. && Ns > 0.)
264  {
265  avgs = sums / Ns;
266  sigs = sum2s / Ns - avgs * avgs;
267  avgp = sump / Np;
268  sigp = sum2p / Np - avgp * avgp;
269  prot_frac = Np / (Np + Ns);
270  solv_frac = 1. - prot_frac;
271  }
272  else
273  {
274  REPORT_ERROR(ERR_NUMERICAL, "empty solvent or protein region");
275  }
276 
277  // Terwilliger-like calculation of P(x|solv) & P(x|prot)
278  // Bayes: P(prot|x)= P(x|prot)/{P(x|prot)+P(x|solv)}
279  double isigs=-1/(2.0*sigs);
280  double isigp=-1/(2.0*sigp);
282  {
283  double voxelVal=A3D_ELEM(mVin, k, i, j);
284  aux = voxelVal - avgs;
285  p_solv = solv_frac * exp(aux * aux * isigs);
286  aux = voxelVal - avgp;
287  p_prot = prot_frac * exp(aux * aux * isigp);
288  A3D_ELEM(mVout, k, i, j) = p_prot / (p_prot + p_solv);
289  }
290 }
291 
292 // Really segment ==========================================================
294 {
295  double th_min, th_max, val_min=0., val_max=0.;
296  V().computeDoubleMinMax(val_min, val_max);
297  th_min = val_min;
298  th_max = val_max;
299 
300  bool ok = false;
301  if (!otsu)
302  {
303  if (!en_threshold)
304  {
305  // Perform a bracketing search until the mass is
306  // within a 0.1% of the desired mass
307  do
308  {
309  double th_med = (th_min + th_max) * 0.5;
310  double mass_med = segment_threshold(&V, &mask, th_med, do_prob);
311  std::cout << "Threshold= " << th_med
312  << " mass of the main piece= " << mass_med << std::endl;
313  if (ABS(mass_med - voxel_mass) / voxel_mass < 0.001)
314  {
315  ok = true;
316  break;
317  }
318  if ((th_max - th_min) / (val_max - val_min) < 0.0001)
319  break;
320  if (mass_med < voxel_mass)
321  {
322  th_max = th_med;
323  }
324  else
325  {
326  th_min = th_med;
327  }
328  }
329  while (true);
330  }
331  else
332  {
333  // Perform a single thresholding
334  double mass_med = segment_threshold(&V, &mask, threshold, do_prob);
335  std::cout << "Threshold= " << threshold
336  << " mass of the main piece= " << mass_med << std::endl;
337  ok = true;
338  }
339  }
340 
341  if (otsu)
342  {
343  mask()=V();
344  double th=EntropyOtsuSegmentation(mask());
345  std::cout << "Threshold " << th << std::endl;
346  ok=true;
347  }
348 
349  if (do_prob)
350  {
351  // Wang-Leslie like modification of the input volume
352  if (wang_radius >= 3)
353  {
354  Image<double> Vwang;
355  wang_smoothing(&V, &Vwang, wang_radius);
356  V = Vwang;
357  }
358  // Terwilliger-like calculation of P(solv|x) through P(x|solv) & P(x|prot)
359  probabilistic_solvent(&V, &mask);
360  }
361 
362  // Save mask if necessary
363  if (fn_mask != "" && (ok || do_prob))
364  mask.write(fn_mask);
365  if (!ok && !do_prob)
366  std::cout << "Segment: Cannot find an appropriate threshold\n";
367 }
368 
370 {
371  Image<double> mask;
372  show();
374  segment(mask);
375 }
void show() const
Show.
#define VEC_ELEM(v, i)
Definition: matrix1d.h:245
#define XMIPP_MAX(x, y)
Definition: xmipp_macros.h:193
Image< double > V
double getDoubleParam(const char *param, int arg=0)
#define FINISHINGX(v)
#define REPORT_ERROR(nerr, ErrormMsg)
Definition: xmipp_error.h:211
void wang_smoothing(const Image< double > *V_in, Image< double > *V_out, int radius)
doublereal * c
void sqrt(Image< double > &op)
void probabilistic_solvent(Image< double > *V_in, Image< double > *V_out)
void write(const FileName &name="", size_t select_img=ALL_IMAGES, bool isStack=false, int mode=WRITE_OVERWRITE, CastWriteMode castMode=CW_CAST, int _swapWrite=0)
String method
Segmentation method.
FileName fn_mask
Output mask. If not given it is not written.
#define FINISHINGZ(v)
#define STARTINGX(v)
bool en_threshold
Enable a single threshold measure.
#define i
void segment(Image< double > &mask)
ql0001_ & k(htemp+1),(cvec+1),(atemp+1),(bj+1),(bl+1),(bu+1),(x+1),(clamda+1), &iout, infoqp, &zero,(w+1), &lenw,(iw+1), &leniw, &glob_grd.epsmac
int maxIndex() const
Definition: matrix1d.cpp:610
#define STARTINGY(v)
#define A3D_ELEM(V, k, i, j)
#define FOR_ALL_ELEMENTS_IN_ARRAY3D(V)
void opening3D(const MultidimArray< double > &in, MultidimArray< double > &out, int neig, int count, int size)
Definition: morphology.cpp:440
void closing3D(const MultidimArray< double > &in, MultidimArray< double > &out, int neig, int count, int size)
Definition: morphology.cpp:422
const char * getParam(const char *param, int arg=0)
void readParams()
Read arguments.
double EntropyOtsuSegmentation(MultidimArray< double > &V, double percentil, bool binarizeVolume)
Definition: filters.cpp:1145
double segment_threshold(const Image< double > *V_in, Image< double > *V_out, double threshold, bool do_prob)
Error related to numerical calculation.
Definition: xmipp_error.h:179
#define ABS(x)
Definition: xmipp_macros.h:142
double threshold
Threshold.
#define j
FileName fn_vol
Input volume.
#define FINISHINGY(v)
double aa_mass
Desired mass (in aminoacids). Not necessary if voxel_mass is provided.
bool otsu
Use Otse.
double sampling_rate
Sampling rate (in A/pixel). Not necessary if voxel_mass is provided.
void defineParams()
Define parameters.
int read(const FileName &name, DataMode datamode=DATA, size_t select_img=ALL_IMAGES, bool mapData=false, int mode=WRITE_READONLY)
float r2
double dalton_mass
Desired mass (in Daltons). Not necessary if voxel_mass is provided.
int wang_radius
radius for B.C. Wang-like smoothing procedure
int labelImage3D(const MultidimArray< double > &V, MultidimArray< double > &label)
Definition: filters.cpp:669
int getIntParam(const char *param, int arg=0)
Incorrect value received.
Definition: xmipp_error.h:195
#define STARTINGZ(v)
void addParamsLine(const String &line)
bool do_prob
From here on by Sjors.