Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
ImageDistribution.cpp
1 /*
2 This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4 Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10 BIAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14 
15 BIAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19 
20 You should have received a copy of the GNU Lesser General Public License
21 along with BIAS; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 
25 #include "ImageDistribution.hh"
26 
27 using namespace std;
28 using namespace BIAS;
29 
30 void ImageDistribution::MapImagesToFixedSize(vector<Image<unsigned char> > &inputImages,
31  Image<unsigned char> &outputImage,
32  vector<Vector2<unsigned int> > &imagePositionsPixel)
33 {
34 
35  vector<Image<unsigned char>*> inputPtrs;
36 
37  for(unsigned int i=0; i<inputImages.size(); i++){
38  inputPtrs.push_back(&inputImages[i]);
39  }
40 
41  MapImagesToFixedSize(inputPtrs,
42  outputImage,
43  imagePositionsPixel);
44 
45 }
46 
47 void ImageDistribution::MapImagesToFixedSize(const vector<Image<unsigned char>*> &inputImages,
48  Image<unsigned char> &outputImage,
49  vector<Vector2<unsigned int> > &imagePositionsPixel)
50 {
51  cout << "mapping to fixed size..." << endl;
52 
53  // init output
54  outputImage.SetZero();
55  imagePositionsPixel.clear();
56 
57  // assertions and early termination
58  if (inputImages.size() == 0) return;
59 
60  BIASASSERT(inputImages[0]->GetWidth() > 0)
61  BIASASSERT(inputImages[0]->GetHeight() > 0)
62  BIASASSERT(outputImage.GetWidth() > 0)
63  BIASASSERT(outputImage.GetHeight() > 0)
64 
65  // define some vars for better readability
66  const unsigned int &numImages = inputImages.size();
67  const unsigned int &widthIn = inputImages[0]->GetWidth();
68  const unsigned int &heightIn = inputImages[0]->GetHeight();
69 
70  const unsigned int &widthOut = outputImage.GetWidth();
71  const unsigned int &heightOut = outputImage.GetHeight();
72 
73  // compute grid and scale
74  unsigned int numCols;
75  unsigned int numRows;
76  float scale;
77 
78  ComputeGridAndScale(numImages, widthIn, heightIn, widthOut, heightOut,
79  numCols, numRows, scale);
80 
81  // compute image positions
82  vector<Vector2<unsigned int> > imPosGrid;
83  vector<vector<int> > imageIndices;
84 
85  ComputeImagePositions(numImages, widthIn, heightIn, numRows, numCols, scale,
86  imPosGrid, imagePositionsPixel, imageIndices);
87 
88  // map the input images
89  unsigned char* outputImData = outputImage.GetImageData();
90 
91  float widthInScaled = float(widthIn) * scale;
92  float heightInScaled = float(heightIn) * scale;
93 
94  for (unsigned int y = 0; y < heightOut; ++y) {
95  // at the bottom may be unused pixels
96  if (y >= (numRows * heightInScaled)) { break; }
97 
98  // get row in which current pixel lies
99  const unsigned int row = (unsigned)floor(float(y) / heightInScaled);
100 
101  for (unsigned int x = 0; x < widthOut; ++x) {
102  // at the right-hand side may be unused pixels
103  if (x >= (numCols * widthInScaled)) { break; }
104 
105  // get column in which current pixel lies
106  const unsigned int col = (unsigned)floor(float(x) / widthInScaled);
107 
108  // now search for the image in the current row and column
109  const int curImageIndex = imageIndices[col][row];
110 
111  if (curImageIndex == -1) {
112  continue;
113  }
114 
115  // compute the array offsets
116  const unsigned int xImage = (unsigned)rint((x - col * widthInScaled) / scale);
117  const unsigned int yImage = (unsigned)rint((y - row * heightInScaled) / scale);
118 
119  const unsigned int inputOffset
120  = (yImage
121  * inputImages[curImageIndex]->GetWidth()
122  * inputImages[curImageIndex]->GetChannelCount())
123  + (xImage
124  * inputImages[curImageIndex]->GetChannelCount());
125 
126  const unsigned int outputOffset
127  = (y
128  * outputImage.GetWidth()
129  * outputImage.GetChannelCount())
130  + (x
131  * outputImage.GetChannelCount());
132 
133  // finally map pixels
134  const unsigned char* inputImData
135  = inputImages[curImageIndex]->GetImageData();
136 
137  for (unsigned int c = 0; c < outputImage.GetChannelCount(); ++c) {
138  outputImData[outputOffset + c] = inputImData[inputOffset + c];
139  }
140  }
141  }
142 }
143 
144 
145 
146 void ImageDistribution::MapImagesToSquare(vector<Image<unsigned char> > &inputImages,
147  Image<unsigned char> &outputImage,
148  vector<Vector2<unsigned int> > &imagePositionsPixel)
149 {
150  vector<Image<unsigned char>*> inputPtrs;
151 
152  for(unsigned int i=0; i<inputImages.size(); i++){
153  inputPtrs.push_back(&inputImages[i]);
154  }
155 
156  MapImagesToSquare(inputPtrs,
157  outputImage,
158  imagePositionsPixel);
159 }
160 
161 void ImageDistribution::MapImagesToSquare(const vector<Image<unsigned char>*> &inputImages,
162  Image<unsigned char> &outputImage,
163  vector<Vector2<unsigned int> > &imagePositionsPixel)
164 {
165  cout << "mapping to square (scale is ignored)..." << endl;
166 
167  outputImage.SetZero();
168 
169  const unsigned int numImages = inputImages.size();
170  const unsigned int widthIn = inputImages[0]->GetWidth();
171  const unsigned int heightIn = inputImages[0]->GetHeight();
172 
173  // map to unit square and later ignore the scale factor
174  const unsigned int widthOut = 1;
175  const unsigned int heightOut = 1;
176 
177  // compute grid and scale
178  unsigned int numCols;
179  unsigned int numRows;
180  float scale;
181 
182  ComputeGridAndScale(numImages, widthIn, heightIn, widthOut, heightOut,
183  numCols, numRows, scale);
184 
185  // compute image positions
186  vector<Vector2<unsigned int> > imPosGrid;
187  vector<Vector2<unsigned int> > imPosPixel;
188  vector<vector<int> > imageIndices;
189 
190  ComputeImagePositions(numImages, widthIn, heightIn, numRows, numCols, scale,
191  imPosGrid, imPosPixel, imageIndices);
192 
193  // compute size of output image and resize it
194  const unsigned int edgeLength = max(numCols * widthIn, numRows * heightIn);
195 
196  outputImage.Init(edgeLength, edgeLength, inputImages[0]->GetChannelCount());
197 
198  // map the input images
199  unsigned char* outputImData = outputImage.GetImageData();
200 
201  for (size_t imageCount = 0; imageCount < numImages; ++imageCount) {
202  const unsigned char* inputImData = inputImages[imageCount]->GetImageData();
203 
204  const unsigned int xOffset = imPosGrid[imageCount][0] * widthIn;
205  const unsigned int yOffset = imPosGrid[imageCount][1] * heightIn;
206 
207  for (unsigned int y = 0; y < inputImages[imageCount]->GetHeight(); ++y) {
208  for (unsigned int x = 0; x < inputImages[imageCount]->GetWidth(); ++x) {
209  const unsigned int inputOffset
210  = (y
211  * inputImages[imageCount]->GetWidth()
212  * inputImages[imageCount]->GetChannelCount())
213  + (x
214  * inputImages[imageCount]->GetChannelCount());
215 
216  const unsigned int outputOffset
217  = ((y + yOffset)
218  * outputImage.GetWidth()
219  * outputImage.GetChannelCount())
220  + ((x + xOffset)
221  * outputImage.GetChannelCount());
222 
223  for (unsigned int c = 0; c < inputImages[imageCount]->GetChannelCount(); ++c) {
224  outputImData[outputOffset + c] = inputImData[inputOffset + c];
225  }
226  }
227  }
228  }
229 
230  // recompute image positions with scale 1.0
231  ComputeImagePositions(numImages, widthIn, heightIn, numRows, numCols, 1.0,
232  imPosGrid, imagePositionsPixel, imageIndices);
233 }
234 
235 
236 void ImageDistribution::ComputeGridAndScale(unsigned int numImages,
237  unsigned int widthIn,
238  unsigned int heightIn,
239  unsigned int widthOut,
240  unsigned int heightOut,
241  unsigned int &numCols,
242  unsigned int &numRows,
243  float &scale)
244 {
245  numCols = 0;
246  numRows = 0;
247  scale = 0.0;
248 
249  // using floats for the computations
250  const float numImagesF = (float)numImages;
251  const float widthInF = (float)widthIn;
252  const float heightInF = (float)heightIn;
253 
254  const float widthOutF = (float)widthOut;
255  const float heightOutF = (float)heightOut;
256 
257  // find optimal configuration with highest scaling factor
258  for (float tmpNumCols = numImagesF; tmpNumCols >= 1.0; --tmpNumCols) {
259  const float tmpNumRows = ceil(numImagesF / tmpNumCols);
260 
261  // minimun of horizontal or vertical scale
262  const float tmpScale = min(widthOutF / (tmpNumCols * widthInF),
263  heightOutF / (tmpNumRows * heightInF));
264 
265  if (tmpScale > scale) {
266  numCols = (unsigned int)tmpNumCols;
267  numRows = (unsigned int)tmpNumRows;
268  scale = tmpScale;
269  }
270  }
271 
272  cout << "taking " << numCols << "x" << numRows << " @ "
273  << (scale * 100.0) << "%" << endl;
274 }
275 
276 void ImageDistribution::ComputeImagePositions(unsigned int numImages,
277  unsigned int widthIn,
278  unsigned int heightIn,
279  unsigned int numRows,
280  unsigned int numCols,
281  float scale,
282  vector<Vector2<unsigned int> > &imagePositionsGrid,
283  vector<Vector2<unsigned int> > &imagePositionsPixel,
284  vector<vector<int> > &imageIndices)
285 {
286  // using floats for the computations
287  const float numImagesF = (float)numImages;
288  const float widthInF = (float)widthIn;
289  const float heightInF = (float)heightIn;
290 
291  const float numColsF = (float)numCols;
292 
293  // for each grid cell we want to know which image is to be painted inside
294  imageIndices.clear();
295 
296  imageIndices.resize(numCols);
297  for (unsigned int colCount = 0; colCount < numCols; ++colCount) {
298  imageIndices[colCount].resize(numRows, -1);
299  }
300 
301  // compute upper left corner for each image in destination area
302  imagePositionsGrid.clear();
303  imagePositionsGrid.reserve(size_t(numImagesF));
304  imagePositionsPixel.clear();
305  imagePositionsPixel.reserve(size_t(numImagesF));
306 
307  for (size_t imageCount = 0; imageCount < size_t(numImagesF); ++imageCount) {
308  // compute row and column of current image
309  const unsigned int row = (unsigned int)floor(float(imageCount) / numColsF);
310  const unsigned int col = (unsigned int)(imageCount - (row * numColsF));
311 
312  // save grid as well as pixel position and image index
313  imagePositionsGrid.push_back(Vector2<unsigned int>(col, row));
314  imagePositionsPixel.push_back(Vector2<unsigned int>((unsigned)rint(col * widthInF * scale),
315  (unsigned)rint(row * heightInF * scale)));
316  imageIndices[col][row] = imageCount;
317 
318  cout << "image " << imageCount << " is at "
319  << imagePositionsGrid[imageCount][0] << "x" << imagePositionsGrid[imageCount][1]
320  << " ("
321  << imagePositionsPixel[imageCount][0] << "," << imagePositionsPixel[imageCount][1]
322  << ")" << endl;
323  }
324 }
unsigned int GetWidth() const
Definition: ImageBase.hh:312
unsigned int GetChannelCount() const
returns the number of Color channels, e.g.
Definition: ImageBase.hh:382
unsigned int GetHeight() const
Definition: ImageBase.hh:319
void Init(unsigned int Width, unsigned int Height, unsigned int channels=1, enum EStorageType storageType=ST_unsignedchar, const bool interleaved=true)
calls Init from ImageBase storageType is ignored, just dummy argument
Definition: Image.cpp:421
const StorageType * GetImageData() const
overloaded GetImageData() from ImageBase
Definition: Image.hh:137
void SetZero()
zeroes the image
Definition: ImageBase.hh:83