private void TestImage(IEnumerable <Point> arrayOfTestPoints) { if (_trace) { string imageType; if (_sourceImageType == ImageTypes.Mono16) { imageType = "Mono16"; } else if (_sourceImageType == ImageTypes.Mono8) { imageType = "Mono8"; } else if (_sourceImageType == ImageTypes.Mono8Signed) { imageType = "Mono8Signed"; } else if (_sourceImageType == ImageTypes.Mono16Signed) { imageType = "Mono16Signed"; } else { imageType = "Rgb"; } TraceLine(""); TraceLine(imageType); TraceLine(String.Format("Scale (Fit/Scale): {0}/{1}", _scaleToFit, _scale)); TraceLine(String.Format("Orientation(FH/FV/R): {0}/{1}/{2}", _flipHorizontal, _flipVertical, _rotation)); TraceLine(String.Format("Translation: {0}, {1}", _translationX, _translationY)); } CreateImageLayer(_sourceImageType); CreatePhantom(); //render the image to a bitmap Bitmap dstBitmap = ImageRendererTestUtilities.RenderLayer(_image, _dstWidth, _dstHeight); if (_traceBitmap) { string strTraceBitmap = "Bitmap:\n"; for (int y = 0; y < dstBitmap.Height; y++) { for (int x = 0; x < dstBitmap.Width; x++) { byte pixelValue = dstBitmap.GetPixel(x, y).R; strTraceBitmap += String.Format("{0} ", (int)pixelValue); } strTraceBitmap += "\n"; } TraceLine(strTraceBitmap); } foreach (Point dstPoint in arrayOfTestPoints) { // //The point of the unit test here is to do the same bilinear calculation as is done in the // //actual interpolation code, but in a more reliable & simpler way (e.g. not using pointer // //arithmetic, offsets, rectangles, etc). Rectangle dstViewableRectangle; RectangleF srcViewableRectangle; ImageRenderer.CalculateVisibleRectangles(_image, new Rectangle(0, 0, _dstWidth, _dstHeight), out dstViewableRectangle, out srcViewableRectangle); byte dstValue = dstBitmap.GetPixel(dstPoint.X, dstPoint.Y).R; //just check the value of R. if (dstViewableRectangle.Contains(dstPoint)) { PointF dstTestPoint = new PointF(dstPoint.X + 0.5F, dstPoint.Y + 0.5F); PointF srcTestPoint = _image.SpatialTransform.ConvertToSource(dstTestPoint); float tolerance = 1 / (8F * _fixedScale); // We can take advantage of the fact that we are using fixed point arithmetic to do the interpolation // because it essentially means that there is a discrete set of resulting values for a given 4-pixel // region (dx,dy between 0.0 -> 1.0 translates to 0/128 -> 128/128 in fixed point). // Because we use the SpatialTransform class to calculate the source coordinate in the C# world, // and 2 rectangles in the C++ world, we can't expect to get *exactly* the same answer in both cases. // Therefore, we calculate the values at source points offset by a tolerance much smaller than 1/128, // so that if by chance the source coordinates are teetering on a fixed-point boundary (e.g. slight // changes would result in dx or dy being off by +-1/128) then we can still be confident that the // source coordinates the interpolator is calculating are reasonable, and thus the interpolated value // is correct, even if it's not exactly what we calculate in C# with zero-tolerance. // The tolerance used here is 1/(8*128) ~= 0.001. When the test passes, this means that the C# and // C++ calculations of the source pixel coordinate (and thus, dx/dy and the interpolated value) agree to // within one one-thousandth of a pixel. List <SizeF> offsets = new List <SizeF>(); offsets.Add(new SizeF(0F, 0F)); offsets.Add(new SizeF(-tolerance, -tolerance)); offsets.Add(new SizeF(-tolerance, +tolerance)); offsets.Add(new SizeF(+tolerance, -tolerance)); offsets.Add(new SizeF(+tolerance, +tolerance)); bool success = false; int i = 0; foreach (SizeF offset in offsets) { PointF srcPoint00 = PointF.Add(srcTestPoint, offset); byte backCalculateValue = PerformBilinearInterpolationAt(srcPoint00); if (_trace) { string strMessage = String.Format("Test Point #{0} ({1}, {2}): {3}, BackCalculated({4:F16}, {5:F16}): {6}\n", ++i, dstTestPoint.X, dstTestPoint.Y, dstValue, srcPoint00.X, srcPoint00.Y, backCalculateValue); TraceLine(strMessage); } if (backCalculateValue == dstValue) { success = true; break; } } Assert.IsTrue(success, "Failed for all test points within tolerance range."); } else { //The bilinear interpolation algorithm should not calculate any values outside the dstViewableRectangle. string strMessage = String.Format("Point outside rectangle ({0}, {1}) = {2}", dstPoint.X, dstPoint.Y, dstValue); if (_trace) { TraceLine(strMessage); } Assert.AreEqual(0, dstValue, strMessage); } } }