Xmipp  v3.23.11-Nereus
transform_downsample.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: Carlos Oscar S. Sorzano (coss@cnb.csic.es)
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/xmipp_fftw.h"
28 #include "core/xvsmooth.h"
29 #include "transform_downsample.h"
30 
31 // Read --------------------------------------------------------------------
33 {
34 
36  step=getDoubleParam("--step");
37  String strMethod=getParam("--method");
38  if (strMethod=="fourier")
39  {
40  nThreads=getIntParam("--method",1);
42  }
43  else if (strMethod=="smooth")
44  method=SMOOTH;
45  else
47 }
48 
49 // Usage -------------------------------------------------------------------
51 {
53  addUsageLine("Downsample a micrograph. For volumes use xmipp_transform_geometry.");
54  addUsageLine("+There are several downsampling methods. The most general and recommended is Fourier.");
55  addUsageLine("+Fourier downsampling puts a window in Fourier space. This is the best downsampling that can be performed.");
56  addUsageLine("+Altermatively, smoothing makes color dithering which is pretty good for visualization, ");
57  addUsageLine("+but it modifies the particle spectrum. Binning with a rectangle kernel modifies the ");
58  addUsageLine("+spectrum of the micrographs and is not recommended. You may see the effects of the different ");
59  addUsageLine("+downsampling schemes at [[http://biocomp.cnb.csic.es/~coss/Articulos/Sorzano2009d.pdf][this article]].");
60  addUsageLine("+ ");
61  addUsageLine("+The downsampling factor (--step) is the factor by which the micrograph will be reduced.");
62  addUsageLine("+For instance, a downsampling by 2 will reduce the image size to one half. Using Fourier and smooth ");
63  addUsageLine("+you may use non-integer downsampling factors, and the image size will be reduced by 1/factor");
64  addSeeAlsoLine("transform_geometry");
66  addParamsLine(" --step <factor> : Downsampling factor. factor=2 reduces the image size to one half.");
67  addParamsLine(" :+Fourier and smooth support non-integer downsampling factors.");
68  addParamsLine(" :+Rectangular binning must use integer factors.");
69  addParamsLine(" [--method <mth=fourier>] : Method for making the downsampling");
70  addParamsLine(" where <mth>");
71  addParamsLine(" fourier <numThreads=1>: Fourier supports non-integer downsampling factors");
72  addParamsLine(" :+This is the best choice.");
73  addParamsLine(" rectangle: This is simple binning in a square of size factor x factor");
74  addParamsLine(" :+This is not a good choice since it creates aliasing and ");
75  addParamsLine(" :+unequal frequency damping.");
76  addParamsLine(" smooth: smooth and colordither");
77  addParamsLine(" :+ Both input and output micrographs must be 8 bits, unsigned char");
78  addExampleLine("xmipp_transform_downsample -i micrograph.tif -o downsampledMicrograph.tif --step 2");
79 }
80 
81 // Downsample micrograph ---------------------------------------------------
82 void ProgTransformDownsample::processImage(const FileName &fnImg, const FileName &fnImgOut, const MDRow &rowIn, MDRow &rowOut)
83 {
84  // Open input data
85  ImageGeneric M_in;
86  M_in.readOrReadMapped(fnImg);
87  size_t Zdim, Ydim, Xdim;
88  M_in.getDimensions(Xdim,Ydim,Zdim);
89  if (Zdim!=1)
90  REPORT_ERROR(ERR_MULTIDIM_DIM,"This program is not intended for volumes");
91 
92  // Open output data, mapped file
93  auto Xpdim = (int)floor(Xdim/step);
94  auto Ypdim = (int)floor(Ydim/step);
95  ImageGeneric M_out;
96  if (method==SMOOTH)
97  M_out.setDatatype(DT_UChar);
98  else
99  M_out.setDatatype(DT_Float);
100  M_out.mapFile2Write(Xpdim, Ypdim, 1, fnImgOut,fnImg==fnImgOut);
101 
102  // Downsample
103  if (method==KER_RECTANGLE)
104  downsampleKernel(M_in,step,M_out);
105  else if (method==FOURIER)
106  downsampleFourier(M_in,step,M_out,nThreads);
107  else
108  downsampleSmooth(M_in,M_out);
109 
110  M_out.write(fnImgOut);
111 }
112 
113 /* Downsample -------------------------------------------------------------- */
114 void downsampleKernel(const ImageGeneric &M, double step, ImageGeneric &Mp)
115 {
116  auto istep=(int)step;
118  kernel.resizeNoCopy(istep,istep);
119  kernel.initConstant(1.0/MULTIDIM_SIZE(kernel));
120 
121  size_t Ydim, Xdim, Ypdim, Xpdim;
122  M().getDimensions(Xdim, Ydim);
123  Mp().getDimensions(Xpdim, Ypdim);
124 
125  // Look for input/output ranges
126  double a = 1;
127  double b = 0;
128  double scale = 1;
129  size_t ii, jj, i2, j2, i, j, y, x;
130  if (Mp.getDatatype() != DT_Float)
131  {
132  double imin, imax;
133  double omin=0., omax=0.;
134  imin=imax=M.getPixel(0,0);
135  bool ofirst = true;
136 
137  if (M.getDatatype() != DT_Float)
138  scale = (pow(2.0, Mp.getDatatypeDepth()) - 1.0) /
139  (pow(2.0, M.getDatatypeDepth()) - 1.0);
140  else if (M.getDatatype() == DT_Float)
141  scale = 1;
142  for (ii = 0, y = 0; y < Ydim && ii < Ypdim; y += istep, ++ii)
143  for (jj = 0, x = 0; x < Xdim && jj < Xpdim; x += istep, ++jj)
144  {
145  double pixval = 0;
146  for (i=0, i2=y; i<YSIZE(kernel) && i2<Ydim; ++i, ++i2)
147  for (j=0, j2=x; j<XSIZE(kernel) && j2<Xdim; ++j, ++j2)
148  {
149  double aux=M.getPixel(i2, j2);
150  imin = XMIPP_MIN(imin, aux);
151  imax = XMIPP_MAX(imax, aux);
152  pixval += A2D_ELEM(kernel,i, j) * aux;
153  }
154  pixval *= scale;
155  if (ofirst)
156  {
157  omin = omax = pixval;
158  ofirst = false;
159  }
160  else
161  {
162  omin = XMIPP_MIN(omin, pixval);
163  omax = XMIPP_MAX(omax, pixval);
164  }
165  }
166 
167  // Compute range transformation
168  double irange = imax - imin;
169  double orange = omax - omin;
170 
171  if (M.getDatatype() != DT_Float)
172  {
173  a = scale * irange / orange;
174  b = -omin;
175  }
176  else if (Mp.getDatatype() != DT_Float)
177  {
178  a = (pow(2.0, Mp.getDatatypeDepth()) - 1.0) / orange;
179  scale = 1;
180  b = -omin;
181  }
182  }
183 
184  // Really downsample
185  for (ii = 0, y = 0; y < Ydim && ii < Ypdim; y += istep, ++ii)
186  for (jj = 0, x = 0; x < Xdim && jj < Xpdim; x += istep, ++jj)
187  {
188  double pixval = 0;
189  for (i=0, i2=y; i<YSIZE(kernel) && i2<Ydim; ++i, ++i2)
190  for (j=0, j2=x; j<XSIZE(kernel)&& j2<Xdim; ++j, ++j2)
191  pixval += A2D_ELEM(kernel,i, j) * M.getPixel(i2, j2);
192  if (ii < Ypdim && jj < Xpdim)
193  {
194  if (Mp.datatype != DT_Float)
195  Mp.setPixel(ii, jj, floor(a*(pixval*scale + b)));
196  else
197  Mp.setPixel(ii, jj, pixval);
198  }
199  }
200 }
201 
202 void downsampleFourier(const ImageGeneric &M, double step, ImageGeneric &Mp, int nThreads)
203 {
204  size_t Ydim, Xdim, Ypdim, Xpdim;
205  M().getDimensions(Xdim, Ydim);
206  Mp().getDimensions(Xpdim, Ypdim);
207 
208  // Read the micrograph in memory as doubles
210  Mmem.setMmap(true);
211  Mmem.resizeNoCopy(Ydim,Xdim);
213  M().getImage(Mmem);
214 
215  // Perform the Fourier transform
216  FourierTransformer transformerM;
217  transformerM.setThreadsNumber(nThreads);
218  transformerM.FourierTransform(Mmem, MmemFourier, false);
219 
220  // Create space for the downsampled image and its Fourier transform
221  MultidimArray<double> Mpmem(Ypdim,Xpdim);
222  MultidimArray<std::complex<double> > MpmemFourier;
223  FourierTransformer transformerMp;
224  transformerMp.setThreadsNumber(nThreads);
225  transformerMp.setReal(Mpmem);
226  transformerMp.getFourierAlias(MpmemFourier);
227 
228  int ihalf=YSIZE(MpmemFourier)/2+1;
229  for (int i=0; i<ihalf; i++)
230  for (size_t j=0; j<XSIZE(MpmemFourier); j++)
231  A2D_ELEM(MpmemFourier,i,j)=A2D_ELEM(MmemFourier,i,j);
232  for (size_t i=ihalf; i<YSIZE(MpmemFourier); i++)
233  {
234  size_t ip=YSIZE(MmemFourier)-YSIZE(MpmemFourier)+i;
235  for (size_t j=0; j<XSIZE(MpmemFourier); j++)
236  A2D_ELEM(MpmemFourier,i,j)=A2D_ELEM(MmemFourier,ip,j);
237  }
238 
239  // Transform data
240  transformerMp.inverseFourierTransform();
241 
242  // Find minimun and range in output data
243  double omin=0.,omax=0.;
244  Mpmem.computeDoubleMinMax(omin,omax);
245  double orange = omax - omin;
246  double a = (pow(2.0, Mp.getDatatypeDepth()) - 1.0) / orange;
247  double b = -omin;
248  double scale=1;
249  if (M.getDatatype() != DT_Float)
250  scale = (pow(2.0, Mp.getDatatypeDepth()) - 1.0) /
251  (pow(2.0, M.getDatatypeDepth()) - 1.0);
252  else if (M.getDatatype() == DT_Float)
253  scale = 1;
254 
255  // Copy back data
256  if (Mp.datatype!=DT_Float)
258  Mp.setPixel(i, j, a*(A2D_ELEM(Mpmem,i,j)*scale + b));
259  else
261  Mp.setPixel(i, j, A2D_ELEM(Mpmem,i,j));
262 }
263 
265 {
266  if (Mp.datatype!=DT_UChar)
267  REPORT_ERROR(ERR_ARG_INCORRECT,formatString("Smooth downsampling is only valid for 8 bit images. \n"
268  "Choose a supporting 8bit file format different from %s",Mp.image->name().c_str()));
269 
270  size_t Ydim, Xdim, Ypdim, Xpdim;
271  M().getDimensions(Xdim, Ydim);
272  Mp().getDimensions(Xpdim, Ypdim);
273  byte *inputImage=nullptr;
275  if (M.datatype==DT_UChar)
276  M().getArrayPointer(inputImage);
277  else
278  {
279  Maux.setMmap(true);
280  M().getImage(Maux);
281  inputImage=MULTIDIM_ARRAY(Maux);
282  }
283  unsigned char *outputImage;
284  Mp().getArrayPointer(outputImage);
285  SmoothResize(inputImage,outputImage, Xdim, Ydim, Xpdim, Ypdim);
286 }
#define YSIZE(v)
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)
#define A2D_ELEM(v, i, j)
void setPixel(unsigned long n, int k, int i, int j, double value) const
#define XMIPP_MAX(x, y)
Definition: xmipp_macros.h:193
double getDoubleParam(const char *param, int arg=0)
__host__ __device__ float2 floor(const float2 v)
void inverseFourierTransform()
Definition: xmipp_fftw.cpp:329
void setThreadsNumber(int tNumber)
Definition: xmipp_fftw.h:119
#define REPORT_ERROR(nerr, ErrormMsg)
Definition: xmipp_error.h:211
#define MULTIDIM_SIZE(v)
void resizeNoCopy(const MultidimArray< T1 > &v)
static double * y
void processImage(const FileName &fnImg, const FileName &fnImgOut, const MDRow &rowIn, MDRow &rowOut)
#define MULTIDIM_ARRAY(v)
TDownsamplingMethod method
void initConstant(T val)
const FileName & name() const
void SmoothResize(byte *picSrc8, byte *destpic8, size_t swide, size_t shigh, size_t dwide, size_t dhigh)
Definition: xvsmooth.cpp:93
doublereal * x
#define i
DataType getDatatype() const
#define FOR_ALL_ELEMENTS_IN_ARRAY2D(m)
void addSeeAlsoLine(const char *seeAlso)
int nThreads
Number of Threads used in the Fourier Transform.
doublereal * b
const char * getParam(const char *param, int arg=0)
void setReal(MultidimArray< double > &img)
Definition: xmipp_fftw.cpp:129
void getDimensions(size_t &Xdim, size_t &Ydim, size_t &Zdim, size_t &Ndim) const
Incorrect argument received.
Definition: xmipp_error.h:113
void setMmap(bool mmap)
void downsampleFourier(const ImageGeneric &M, double step, ImageGeneric &Mp, int nThreads)
#define XSIZE(v)
void computeDoubleMinMax(double &minval, double &maxval) const
void mapFile2Write(int Xdim, int Ydim, int Zdim, const FileName &_filename, bool createTempFile=false, size_t select_img=APPEND_IMAGE, bool isStack=false, int mode=WRITE_OVERWRITE, int _swapWrite=0)
void addExampleLine(const char *example, bool verbatim=true)
double getPixel(unsigned long n, int k, int i, int j) const
ImageBase * image
void FourierTransform(T &v, T1 &V, bool getCopy=true)
Definition: xmipp_fftw.h:166
#define XMIPP_MIN(x, y)
Definition: xmipp_macros.h:181
#define j
MultidimArray< double > kernel
double step
Downsampling factor.
void downsampleKernel(const ImageGeneric &M, double step, ImageGeneric &Mp)
void setDatatype(DataType _datatype)
void defineParams()
Define params.
void downsampleSmooth(const ImageGeneric &M, ImageGeneric &Mp)
void getFourierAlias(T &V)
Definition: xmipp_fftw.h:207
unsigned char byte
Definition: xvsmooth.cpp:73
std::string String
Definition: xmipp_strings.h:34
int readOrReadMapped(const FileName &name, size_t select_img=ALL_IMAGES, int mode=WRITE_READONLY)
String formatString(const char *format,...)
int getDatatypeDepth() const
Incorrect MultidimArray dimensions.
Definition: xmipp_error.h:173
bool each_image_produces_an_output
Indicate that an output is produced for each image in the input.
void addUsageLine(const char *line, bool verbatim=false)
int getIntParam(const char *param, int arg=0)
doublereal * a
void addParamsLine(const String &line)