/** Checks to see if the two ways of specifying interpolation work */ @Test public void scale_InterpTypeStyle() { ImageFloat32 input = new ImageFloat32(width, height); ImageFloat32 output = new ImageFloat32(width, height); GImageMiscOps.fillUniform(input, rand, 0, 100); DistortImageOps.scale(input, output, TypeInterpolate.BILINEAR); InterpolatePixel<ImageFloat32> interp = FactoryInterpolation.bilinearPixel(input); interp.setImage(input); float scaleX = (float) input.width / (float) output.width; float scaleY = (float) input.height / (float) output.height; if (input.getTypeInfo().isInteger()) { for (int i = 0; i < output.height; i++) { for (int j = 0; j < output.width; j++) { float val = interp.get(j * scaleX, i * scaleY); assertEquals((int) val, output.get(j, i), 1e-4); } } } else { for (int i = 0; i < output.height; i++) { for (int j = 0; j < output.width; j++) { float val = interp.get(j * scaleX, i * scaleY); assertEquals(val, output.get(j, i), 1e-4); } } } }
public static BufferedImage standard(ImageSingleBand<?> src, BufferedImage dst) { if (src.getDataType().isInteger()) { ImageInteger srcInt = (ImageInteger) src; if (src.getDataType().isSigned()) { double max = GImageStatistics.maxAbs(srcInt); return colorizeSign(srcInt, dst, (int) max); } else { if (src.getDataType().getNumBits() == 8) { dst = ConvertBufferedImage.convertTo((ImageUInt8) src, dst); } else { double max = GImageStatistics.maxAbs(srcInt); dst = grayUnsigned(srcInt, dst, (int) max); } } } else if (ImageFloat32.class.isAssignableFrom(src.getClass())) { ImageFloat32 img = (ImageFloat32) src; float max = ImageStatistics.maxAbs(img); boolean hasNegative = false; for (int i = 0; i < img.getHeight(); i++) { for (int j = 0; j < img.getWidth(); j++) { if (img.get(j, i) < 0) { hasNegative = true; break; } } } if (hasNegative) return colorizeSign(img, dst, (int) max); else return grayMagnitude((ImageFloat32) src, dst, max); } return dst; }
private void constructHistograms(double c_x, double c_y, double scale, double orientation) { double c = Math.cos(orientation); double s = Math.sin(orientation); int gridRadius = gridWidth / 2; // This is the distance between samples // The size is computed by finding the width of one block in the grid, then dividing by the // number of samples along that side double sampleUnit = (2.0 * scale * sigmaToRadius) / numSamples; // how wide a grid cell is in pixels double gridCellLength = numSamples * sampleUnit; int gridCellLengthI = (int) (gridCellLength + 0.5); // round to int // System.out.println("-----------------------------------------"); // System.out.println(" cell length "+gridCellLength); // System.out.println(" sampleUnit "+sampleUnit); int allSampleIndex = 0; for (int gy = 0; gy < gridWidth; gy++) { double gridY = (gy - gridRadius) * gridCellLength; for (int gx = 0; gx < gridWidth; gx++) { // top left coordinate of grid in pixels double gridX = (gx - gridRadius) * gridCellLength; // TODO Sample all pixels here for (int sy = 0; sy < numSamples; sy++) { double y = sy * sampleUnit + gridY; for (int sx = 0; sx < numSamples; sx++, allSampleIndex++) { // Sample point in pixels in grid coordinate system double x = sx * sampleUnit + gridX; // Rotate and translate into image pixel coordinates, then round int px = (int) (x * c - y * s + c_x + 0.5); int py = (int) (x * s + y * c + c_y + 0.5); if (image.isInBounds(px, py)) { double dx = derivX.unsafe_get(px, py); double dy = derivY.unsafe_get(px, py); // Gaussian weighting applied to whole sample area double w = gridWeights[allSampleIndex]; // rotate derivative into grid coordinate system double adjX = (dx * c + dy * s) * w; double adjY = (-dx * s + dy * c) * w; addToHistograms( gx - gridRadius, gy - gridRadius, x / gridCellLength, y / gridCellLength, adjX, adjY); } } } } } }
public void checkInner(ImageFloat32 image, int c_x, int c_y, int w, int h) { ImplDescribePointPixelRegionNCC_F32 alg = new ImplDescribePointPixelRegionNCC_F32(w, h); NccFeature desc = new NccFeature(alg.getDescriptorLength()); alg.setImage(image); assertTrue(alg.isInBounds(c_x, c_y)); alg.process(c_x, c_y, desc); int y0 = c_y - h / 2; int x0 = c_x - w / 2; double mean = 0; for (int y = y0; y < y0 + h; y++) { for (int x = x0; x < x0 + w; x++) { mean += image.get(x, y); } } mean /= w * h; double variance = 0; for (int y = y0; y < y0 + h; y++) { for (int x = x0; x < x0 + w; x++) { double a = image.get(x, y) - mean; variance += a * a; } } variance /= w * h; assertEquals(desc.mean, mean, 1e-8); assertEquals(desc.sigma, Math.sqrt(variance), 1e-8); int index = 0; for (int y = y0; y < y0 + h; y++) { for (int x = x0; x < x0 + w; x++, index++) { assertEquals(image.get(x, y) - mean, desc.value[index], 1e-4); } } }
/** * Runs a canny edge detector on the input image given the provided thresholds. If configured to * save a list of trace points then the output image is optional. * * <p>NOTE: Input and output can be the same instance, if the image type allows it. * * @param input Input image. Not modified. * @param threshLow Lower threshold. >= 0. * @param threshHigh Upper threshold. >= 0. * @param output (Might be option) Output binary image. Edge pixels are marked with 1 and * everything else 0. */ public void process(T input, float threshLow, float threshHigh, ImageUInt8 output) { if (threshLow < 0 || threshHigh < 0) throw new IllegalArgumentException("Threshold must be >= zero!"); if (hysteresisMark != null) { if (output == null) throw new IllegalArgumentException( "An output image must be specified when configured to mark edge points"); } // setup internal data structures blurred.reshape(input.width, input.height); derivX.reshape(input.width, input.height); derivY.reshape(input.width, input.height); intensity.reshape(input.width, input.height); suppressed.reshape(input.width, input.height); angle.reshape(input.width, input.height); direction.reshape(input.width, input.height); work.reshape(input.width, input.height); // run canny edge detector blur.process(input, blurred); gradient.process(blurred, derivX, derivY); GGradientToEdgeFeatures.intensityAbs(derivX, derivY, intensity); GGradientToEdgeFeatures.direction(derivX, derivY, angle); GradientToEdgeFeatures.discretizeDirection4(angle, direction); GradientToEdgeFeatures.nonMaxSuppression4(intensity, direction, suppressed); performThresholding(threshLow, threshHigh, output); }
/** Sets a rectangle inside the image with the specified value. */ public static void fillRectangle( ImageFloat32 img, float value, int x0, int y0, int width, int height) { int x1 = x0 + width; int y1 = y0 + height; for (int y = y0; y < y1; y++) { for (int x = x0; x < x1; x++) { if (img.isInBounds(x, y)) img.set(x, y, value); } } }
private void shiftCopy(int offX, int offY, ImageFloat32 src, ImageFloat32 dst) { for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { int xx = x + offX; int yy = y + offY; if (xx >= 0 && xx < src.width && yy >= 0 && yy < src.height) { dst.set(xx, yy, src.get(x, y)); } } } }
@Test public void checkIntensity() { ImageUInt8 input = new ImageUInt8(40, 50); ImageFloat32 intensity = new ImageFloat32(input.width, input.height); int[] offsets = DiscretizedCircle.imageOffsets(3, input.stride); createCircle(4, 5, offsets, minContinuous, detectDifference + 1, input); createCircle(12, 20, offsets, minContinuous, detectDifference + 10, input); alg.process(input, intensity); assertTrue(intensity.get(4, 5) < intensity.get(12, 20)); }
/** * Fills the whole image with the specified pixel value * * @param img An image. * @param value The value that the image is being filled with. */ public static void fill(ImageFloat32 img, float value) { final int h = img.getHeight(); final int w = img.getWidth(); float[] data = img.data; for (int y = 0; y < h; y++) { int index = img.getStartIndex() + y * img.getStride(); for (int x = 0; x < w; x++) { data[index++] = value; } } }
private void checUpSample(int w, int h) { ImageFloat32 input = new ImageFloat32(w, h); ImageFloat32 output = new ImageFloat32(w * 2, h * 2); GImageMiscOps.fillUniform(input, rand, 0, 100); SiftImageScaleSpace.upSample(input, output); for (int i = 0; i < output.height; i++) { for (int j = 0; j < output.width; j++) { assertTrue(input.get(j / 2, i / 2) == output.get(j, i)); } } }
/** * Sets each value in the image to a value drawn from an uniform distribution that has a range of * min <= X < max. */ public static void randomize(ImageFloat32 img, Random rand, float min, float max) { final int h = img.getHeight(); final int w = img.getWidth(); float range = max - min; float[] data = img.data; for (int y = 0; y < h; y++) { int index = img.getStartIndex() + y * img.getStride(); for (int x = 0; x < w; x++) { data[index++] = rand.nextFloat() * range + min; } } }
/** * Computes the mean squared error (MSE) between the two images. * * @param imgA first image. Not modified. * @param imgB second image. Not modified. * @return error between the two images. */ public static double computeMeanSquaredError(ImageFloat32 imgA, ImageFloat32 imgB) { final int h = imgA.getHeight(); final int w = imgA.getWidth(); double total = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { double difference = imgA.get(x, y) - imgB.get(x, y); total += difference * difference; } } return total / (w * h); }
private static BufferedImage disparity( ImageFloat32 src, BufferedImage dst, int minValue, int maxValue, int invalidColor) { float range = maxValue - minValue; for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { float v = src.unsafe_get(x, y); if (v > range) { dst.setRGB(x, y, invalidColor); } else { int r, b; if (v == 0) { r = b = 0; } else { r = (int) (255 * v / maxValue); b = (int) (255 * (maxValue - v) / maxValue); } dst.setRGB(x, y, r << 16 | b); } } } return dst; }
/** Flips the image from top to bottom */ public static void flipVertical(ImageFloat32 img) { int h2 = img.height / 2; for (int y = 0; y < h2; y++) { int index1 = img.getStartIndex() + y * img.getStride(); int index2 = img.getStartIndex() + (img.height - y - 1) * img.getStride(); int end = index1 + img.width; while (index1 < end) { float tmp = img.data[index1]; img.data[index1++] = img.data[index2]; img.data[index2++] = (float) tmp; } } }
public void checkBorder(ImageFloat32 image, int c_x, int c_y, int w, int h) { ImplDescribePointPixelRegion_F32 alg = new ImplDescribePointPixelRegion_F32(w, h); TupleDesc_F32 desc = new TupleDesc_F32(alg.getDescriptorLength()); alg.setImage(image); alg.process(c_x, c_y, desc); int index = 0; int y0 = c_y - h / 2; int x0 = c_x - w / 2; for (int y = y0; y < y0 + h; y++) { for (int x = x0; x < x0 + w; x++, index++) { if (image.isInBounds(x, y)) assertEquals(image.get(x, y), desc.value[index], 1e-4); else assertEquals(0, desc.value[index], 1e-4); } } }
/** Adds Gaussian/normal i.i.d noise to each pixel in the image. */ public static void addGaussian( ImageFloat32 img, Random rand, double sigma, float min, float max) { final int h = img.getHeight(); final int w = img.getWidth(); float[] data = img.data; for (int y = 0; y < h; y++) { int index = img.getStartIndex() + y * img.getStride(); for (int x = 0; x < w; x++) { float value = (data[index]) + (float) (rand.nextGaussian() * sigma); if (value < min) value = min; if (value > max) value = max; data[index++] = value; } } }
@Test public void scaleSanityCheck() { ImageFloat32 input = new ImageFloat32(width, height); ImageFloat32 output = new ImageFloat32(width / 2, height / 2); GImageMiscOps.fillUniform(input, rand, 0, 100); DistortImageOps.scale(input, output, TypeInterpolate.BILINEAR); double error = 0; for (int y = 0; y < output.height; y++) { for (int x = 0; x < output.width; x++) { double e = input.get(x * 2, y * 2) - output.get(x, y); error += Math.abs(e); } } assertTrue(error / (output.width * output.height) < 0.1); }
public static void set(ImageSingleBand img, int x, int y, double value) { if (ImageInteger.class.isAssignableFrom(img.getClass())) { ((ImageInteger) img).set(x, y, (int) value); } else if (img instanceof ImageFloat32) { ((ImageFloat32) img).set(x, y, (float) value); } else if (img instanceof ImageFloat64) { ((ImageFloat64) img).set(x, y, value); } else { throw new IllegalArgumentException( "Unknown or incompatible image type: " + img.getClass().getSimpleName()); } }
/** Very simple test for rotation accuracy. */ @Test public void rotate_SanityCheck() { ImageFloat32 input = new ImageFloat32(width, height); ImageFloat32 output = new ImageFloat32(height, width); GImageMiscOps.fillUniform(input, rand, 0, 100); DistortImageOps.rotate(input, output, TypeInterpolate.BILINEAR, (float) Math.PI / 2f); double error = 0; // the outside pixels are ignored because numerical round off can cause those to be skipped for (int y = 1; y < input.height - 1; y++) { for (int x = 1; x < input.width - 1; x++) { int xx = output.width - y; int yy = x; double e = input.get(x, y) - output.get(xx, yy); error += Math.abs(e); } } assertTrue(error / (width * height) < 0.1); }
/** * Checks to see if the BufferedImage has the same intensity values as the ImageUInt8 * * @param imgA BufferedImage * @param imgB ImageUInt8 */ public static void checkEquals(BufferedImage imgA, ImageFloat32 imgB, float tol) { if (imgA.getRaster() instanceof ByteInterleavedRaster && imgA.getType() != BufferedImage.TYPE_BYTE_INDEXED) { ByteInterleavedRaster raster = (ByteInterleavedRaster) imgA.getRaster(); if (raster.getNumBands() == 1) { int strideA = raster.getScanlineStride(); int offsetA = raster.getDataOffset(0) - raster.getNumBands() + 1; // handle a special case where the RGB conversion is screwed for (int i = 0; i < imgA.getHeight(); i++) { for (int j = 0; j < imgA.getWidth(); j++) { float valB = imgB.get(j, i); int valA = raster.getDataStorage()[offsetA + i * strideA + j]; valA &= 0xFF; if (Math.abs(valA - valB) > tol) throw new RuntimeException("Images are not equal: A = " + valA + " B = " + valB); } } return; } } for (int y = 0; y < imgA.getHeight(); y++) { for (int x = 0; x < imgA.getWidth(); x++) { int rgb = imgA.getRGB(x, y); float gray = (((rgb >>> 16) & 0xFF) + ((rgb >>> 8) & 0xFF) + (rgb & 0xFF)) / 3.0f; float grayB = imgB.get(x, y); if (Math.abs(gray - grayB) > tol) { throw new RuntimeException("images are not equal: A = " + gray + " B = " + grayB); } } } }
private static BufferedImage grayMagnitude( ImageFloat32 src, BufferedImage dst, float maxAbsValue) { for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { float v = Math.abs(src.get(x, y)); int rgb = (int) (255 * v / maxAbsValue); dst.setRGB(x, y, rgb << 16 | rgb << 8 | rgb); } } return dst; }
@Test public void checkRender() { // Easier to make up a plane in this direction Se3_F64 cameraToPlane = new Se3_F64(); ConvertRotation3D_F64.eulerToMatrix( EulerType.XYZ, UtilAngle.degreeToRadian(0), 0, 0, cameraToPlane.getR()); cameraToPlane.getT().set(0, -5, 0); Se3_F64 planeToCamera = cameraToPlane.invert(null); CreateSyntheticOverheadViewMS<ImageFloat32> alg = new CreateSyntheticOverheadViewMS<ImageFloat32>( TypeInterpolate.BILINEAR, 3, ImageFloat32.class); alg.configure(param, planeToCamera, centerX, centerY, cellSize, overheadW, overheadH); MultiSpectral<ImageFloat32> input = new MultiSpectral<ImageFloat32>(ImageFloat32.class, width, height, 3); for (int i = 0; i < 3; i++) ImageMiscOps.fill(input.getBand(i), 10 + i); MultiSpectral<ImageFloat32> output = new MultiSpectral<ImageFloat32>(ImageFloat32.class, overheadW, overheadH, 3); alg.process(input, output); for (int i = 0; i < 3; i++) { ImageFloat32 o = output.getBand(i); // check parts that shouldn't be in view assertEquals(0, o.get(0, 300), 1e-8); assertEquals(0, o.get(5, 0), 1e-8); assertEquals(0, o.get(5, 599), 1e-8); // check areas that should be in view assertEquals(10 + i, o.get(499, 300), 1e-8); } }
public synchronized void process(final BufferedImage input) { this.input = input; workImage = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_RGB); gray.reshape(input.getWidth(), input.getHeight()); ConvertBufferedImage.convertFrom(input, gray, true); detectTarget(); SwingUtilities.invokeLater( new Runnable() { public void run() { gui.setPreferredSize(new Dimension(input.getWidth(), input.getHeight())); renderOutput(); } }); }
public boolean estimateCameraPose(BufferedImage leftEye) { if (!hasIntrinsic) return false; gray.reshape(leftEye.getWidth(), leftEye.getHeight()); ConvertBufferedImage.convertFrom(leftEye, gray); if (!target.process(gray)) return false; if (!computeH.computeHomography(target.getDetectedPoints())) return false; DenseMatrix64F H = computeH.getHomography(); targetToOrigin.set(decomposeH.decompose(H)); return true; }
public static BufferedImage graySign(ImageFloat32 src, BufferedImage dst, float maxAbsValue) { dst = checkInputs(src, dst); if (maxAbsValue < 0) maxAbsValue = ImageStatistics.maxAbs(src); for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { float v = src.get(x, y); int rgb = 127 + (int) (127 * v / maxAbsValue); dst.setRGB(x, y, rgb << 16 | rgb << 8 | rgb); } } return dst; }
private static BufferedImage colorizeSign( ImageFloat32 src, BufferedImage dst, float maxAbsValue) { for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { float v = src.get(x, y); int rgb; if (v > 0) { rgb = (int) (255 * v / maxAbsValue) << 16; } else { rgb = (int) (-255 * v / maxAbsValue) << 8; } dst.setRGB(x, y, rgb); } } return dst; }
@Override public void renderTarget(ImageFloat32 original, List<CalibrationObservation> solutions) { ImageMiscOps.fill(original, 255); int numRows = config.numRows * 2 - 1; int numCols = config.numCols * 2 - 1; int square = original.getWidth() / (Math.max(numRows, numCols) + 4); int targetWidth = square * numCols; int targetHeight = square * numRows; int x0 = (original.width - targetWidth) / 2; int y0 = (original.height - targetHeight) / 2; for (int i = 0; i < numRows; i += 2) { int y = y0 + i * square; for (int j = 0; j < numCols; j += 2) { int x = x0 + j * square; ImageMiscOps.fillRectangle(original, 0, x, y, square, square); } } int pointsRow = numRows + 1; int pointsCol = numCols + 1; CalibrationObservation set = new CalibrationObservation(); int gridIndex = 0; for (int i = 0; i < pointsRow; i++) { for (int j = 0; j < pointsCol; j++, gridIndex++) { double y = y0 + i * square; double x = x0 + j * square; set.add(new Point2D_F64(x, y), gridIndex); } } solutions.add(set); }
public static void process(ImageFloat32 input, ImageFloat32 output, int radius, float storage[]) { int w = 2 * radius + 1; if (storage == null) { storage = new float[w * w]; } else if (storage.length < w * w) { throw new IllegalArgumentException("'storage' must be at least of length " + (w * w)); } for (int y = 0; y < radius; y++) { int minI = y - radius; int maxI = y + radius + 1; if (minI < 0) minI = 0; if (maxI > input.height) maxI = input.height; for (int x = 0; x < input.width; x++) { int minJ = x - radius; int maxJ = x + radius + 1; // bound it ot be inside the image if (minJ < 0) minJ = 0; if (maxJ > input.width) maxJ = input.width; int index = 0; for (int i = minI; i < maxI; i++) { for (int j = minJ; j < maxJ; j++) { storage[index++] = input.get(j, i); } } // use quick select to avoid sorting the whole list float median = QuickSelectArray.select(storage, index / 2, index); output.set(x, y, median); } } for (int y = input.height - radius; y < input.height; y++) { int minI = y - radius; int maxI = y + radius + 1; if (minI < 0) minI = 0; if (maxI > input.height) maxI = input.height; for (int x = 0; x < input.width; x++) { int minJ = x - radius; int maxJ = x + radius + 1; // bound it ot be inside the image if (minJ < 0) minJ = 0; if (maxJ > input.width) maxJ = input.width; int index = 0; for (int i = minI; i < maxI; i++) { for (int j = minJ; j < maxJ; j++) { storage[index++] = input.get(j, i); } } // use quick select to avoid sorting the whole list float median = QuickSelectArray.select(storage, index / 2, index); output.set(x, y, median); } } for (int y = radius; y < input.height - radius; y++) { int minI = y - radius; int maxI = y + radius + 1; for (int x = 0; x < radius; x++) { int minJ = x - radius; int maxJ = x + radius + 1; // bound it ot be inside the image if (minJ < 0) minJ = 0; if (maxJ > input.width) maxJ = input.width; int index = 0; for (int i = minI; i < maxI; i++) { for (int j = minJ; j < maxJ; j++) { storage[index++] = input.get(j, i); } } // use quick select to avoid sorting the whole list float median = QuickSelectArray.select(storage, index / 2, index); output.set(x, y, median); } } for (int y = radius; y < input.height - radius; y++) { int minI = y - radius; int maxI = y + radius + 1; for (int x = input.width - radius; x < input.width; x++) { int minJ = x - radius; int maxJ = x + radius + 1; // bound it ot be inside the image if (minJ < 0) minJ = 0; if (maxJ > input.width) maxJ = input.width; int index = 0; for (int i = minI; i < maxI; i++) { for (int j = minJ; j < maxJ; j++) { storage[index++] = input.get(j, i); } } // use quick select to avoid sorting the whole list float median = QuickSelectArray.select(storage, index / 2, index); output.set(x, y, median); } } }