private void PreProcess(IImageReader <T2> reader, ImageFormat imageFormat, uint amount, uint?desiredSize)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            if (amount > 1)
            {
                // calculate hashes of images for the defined amount for each axis
                IList <PositionAxisContainerModel <string> > hashes = new List <PositionAxisContainerModel <string> >();
                IDictionary <AxisType, uint[]> newMap = new Dictionary <AxisType, uint[]>();

                IAxisPositionMapper mapper = reader.Mapper;
                if (mapper == null)
                {
                    throw new UnexpectedNullException("The image reader does not contain a mapper.");
                }

                foreach (AxisType axisType in (AxisType[])Enum.GetValues(typeof(AxisType)))
                {
                    newMap[axisType] = new uint[amount];

                    for (uint i = 0; i < amount; i++)
                    {
                        // set initial mapped positions
                        newMap[axisType][i] = mapper.GetMappedPosition(axisType, i);

                        using (var bitmap = reader.ExtractPosition(axisType, i, desiredSize))
                        {
                            if (bitmap != null)
                            {
                                string hash = Cryptography.CalculateSHA1Hash(bitmap.ToByteArray(imageFormat));
                                hashes.Add(new PositionAxisContainerModel <string>(i, axisType, hash));
                            }
                        }
                    }
                }

                // calculate new mapped positions based on the hashes
                foreach (AxisType axisType in (AxisType[])Enum.GetValues(typeof(AxisType)))
                {
                    var axisHashes = hashes.Where(e => e.AxisType == axisType);

                    // find the most likely hash representing an empty image
                    string emptyHash = axisHashes.GroupBy(e => e.Entity)
                                       .Where(e => e.Count() > 1).OrderBy(e => e.Count()).Select(e => e.Key).FirstOrDefault();

                    if (emptyHash != null)
                    {
                        var nonEmptyHashes = axisHashes.Where(e => e.Entity != emptyHash).OrderBy(e => e.Position);

                        var startPosition = nonEmptyHashes.FirstOrDefault();
                        var endPosition   = nonEmptyHashes.LastOrDefault();

                        if (startPosition != null && endPosition != null)
                        {
                            if (startPosition == endPosition)
                            {
                                // set the same mappedPosition for all positions
                                for (int i = 0; i < amount; i++)
                                {
                                    newMap[axisType][i] = mapper.GetMappedPosition(axisType, startPosition.Position);
                                }
                            }
                            else
                            {
                                // calculate mappedPosition between startPosition and endPosition
                                uint mappedStartPosition = mapper.GetMappedPosition(axisType, startPosition.Position);
                                uint mappedEndPosition   = mapper.GetMappedPosition(axisType, endPosition.Position);
                                uint length = mappedEndPosition - mappedStartPosition;

                                for (uint i = 0; i < amount; i++)
                                {
                                    newMap[axisType][i] = mappedStartPosition + mapper.CalculateMappedPosition(amount, length, i);
                                }
                            }
                        }
                    }
                }

                // TODO: distribute equally (newMap) to avoid re-use of the same image excessively compared to others

                /*
                 * Request:
                 * - Extract x images and each image at least once.
                 * Special case:
                 * - Less than x images can be extracted.
                 * Preferred behaviour:
                 * - Prioritize images in the middle for re-use.
                 */

                // set a new mapper with the new map
                reader.Mapper = new AxisPositionMapper(newMap);
            }
        }