All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.gui.image.VisualizeImageData Maven / Gradle / Ivy

Go to download

BoofCV is an open source Java library for real-time computer vision and robotics applications.

There is a newer version: 1.1.7
Show newest version
/*
 * Copyright (c) 2023, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.gui.image;

import boofcv.alg.InputSanityCheck;
import boofcv.alg.misc.GImageStatistics;
import boofcv.alg.misc.ImageStatistics;
import boofcv.io.image.ConvertBufferedImage;
import boofcv.io.image.ConvertRaster;
import boofcv.struct.image.*;
import org.jetbrains.annotations.Nullable;

import java.awt.image.*;

import static boofcv.io.image.ConvertRaster.isKnownByteFormat;

/**
 * Renders different primitive image types into a BufferedImage for visualization purposes.
 *
 * @author Peter Abeles
 */
public class VisualizeImageData {

	public static BufferedImage standard( ImageGray src, BufferedImage dst ) {
		if (src.getDataType().isInteger()) {
			GrayI srcInt = (GrayI)src;

			if (src.getDataType().isSigned()) {
				dst = checkInputs(src, dst);
				double max = GImageStatistics.maxAbs(srcInt);
				colorizeSign(srcInt, dst, (int)max);
			} else {
				if (src.getDataType().getNumBits() == 8) {
					dst = ConvertBufferedImage.convertTo((GrayU8)src, dst);
				} else {
					double max = GImageStatistics.maxAbs(srcInt);
					dst = grayUnsigned(srcInt, dst, (int)max);
				}
			}
		} else if (GrayF32.class.isAssignableFrom(src.getClass())) {
			GrayF32 img = (GrayF32)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)
				colorizeSign(img, dst, (int)max);
			else
				grayMagnitude((GrayF32)src, dst, max);
		}

		return dst;
	}

	/**
	 * 

* Renders a colored image where the color indicates the sign and intensity its magnitude. The input is divided * by normalize to render it in the appropriate scale. *

* * @param src Input single band image. * @param dst Where the image is rendered into. If null a new BufferedImage will be created and return. * @param normalize Used to normalize the input image. If ≤ 0 then the max value will be used * @return Rendered image. */ public static BufferedImage colorizeSign( ImageGray src, @Nullable BufferedImage dst, double normalize ) { dst = checkInputs(src, dst); if (normalize <= 0) { normalize = GImageStatistics.maxAbs(src); } if (normalize == 0) { // sets the output to black ConvertBufferedImage.convertTo(src, dst, true); return dst; } if (src.getClass().isAssignableFrom(GrayF32.class)) { colorizeSign((GrayF32)src, dst, (float)normalize); } else { colorizeSign((GrayI)src, dst, (int)normalize); } return dst; } private static void colorizeSign( GrayI src, BufferedImage dst, int normalize ) { for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { int v = src.get(x, y); int rgb; if (v > 0) { rgb = ((255*v/normalize) << 16); } else { rgb = -((255*v/normalize) << 8); } dst.setRGB(x, y, rgb); } } } public static BufferedImage grayUnsigned( GrayI src, BufferedImage dst, int normalize ) { dst = checkInputs(src, dst); if (src.getDataType().isSigned()) throw new IllegalArgumentException("Can only convert unsigned images."); for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { int v = src.get(x, y); int rgb = 255*v/normalize; dst.setRGB(x, y, rgb << 16 | rgb << 8 | rgb); } } return dst; } /** *

* Renders a gray scale image of the input image's intensity.
*
* dst(i,j) = 255*abs(src(i,j))/normalize *

* * @param src Input single band image. * @param dst Where the image is rendered into. If null a new BufferedImage will be created and return. * @param normalize Used to normalize the input image. If < 0 then this value is automatically computed. * @return Rendered image. */ public static BufferedImage grayMagnitude( ImageGray src, @Nullable BufferedImage dst, double normalize ) { if (normalize < 0) normalize = GImageStatistics.maxAbs(src); dst = checkInputs(src, dst); if (src.getDataType().isInteger()) { grayMagnitude((GrayI)src, dst, (int)normalize); } else if (src instanceof GrayF32) { grayMagnitude((GrayF32)src, dst, (float)normalize); } else if (src instanceof GrayF64) { grayMagnitude((GrayF64)src, dst, (float)normalize); } else { throw new RuntimeException("Unsupported type"); } return dst; } /** *

* Renders a gray scale image using color values from cold to hot. *

* * @param src Input single band image. * @param dst Where the image is rendered into. If null a new BufferedImage will be created and return. * @param normalize Used to normalize the input image. */ public static void grayMagnitudeTemp( ImageGray src, BufferedImage dst, double normalize ) { if (normalize < 0) normalize = GImageStatistics.maxAbs(src); dst = checkInputs(src, dst); if (src.getDataType().isInteger()) { grayMagnitudeTemp((GrayI)src, dst, (int)normalize); } else { throw new RuntimeException("Add support"); } } private static void grayMagnitude( GrayI src, BufferedImage dst, int maxValue ) { for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { int v = Math.abs(src.get(x, y)); int rgb = 255*v/maxValue; dst.setRGB(x, y, rgb << 16 | rgb << 8 | rgb); } } } private static void grayMagnitudeTemp( GrayI src, BufferedImage dst, int maxValue ) { // int halfValue = maxValue / 2 + maxValue % 2; for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { int v = Math.abs(src.get(x, y)); int r, b; if (v == 0) { r = b = 0; } else { r = 255*v/maxValue; b = 255*(maxValue - v)/maxValue; } dst.setRGB(x, y, r << 16 | b); } } } /** *

* Renders a gray scale image using color values from cold to hot. *

* * @param disparity Input disparity image * @param dst Where the image is rendered into. If null a new BufferedImage will be created and return. * @param disparityRange Number of possible disparity values * @param invalidColor RGB value for invalid pixels. Try 0xFF << 8 for green * @return Rendered image. */ public static BufferedImage disparity( ImageGray disparity, @Nullable BufferedImage dst, int disparityRange, int invalidColor ) { dst = ConvertBufferedImage.checkDeclare(disparity.width, disparity.height, dst, BufferedImage.TYPE_INT_RGB); if (disparity.getDataType().isInteger()) { return disparity((GrayI)disparity, dst, disparityRange, invalidColor); } else if (disparity instanceof GrayF32) { return disparity((GrayF32)disparity, dst, disparityRange, invalidColor); } else { throw new RuntimeException("Add support"); } } private static BufferedImage disparity( GrayI src, BufferedImage dst, int range, int invalidColor ) { for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { int 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 = 255*v/range; b = 255*(range - v)/range; } dst.setRGB(x, y, r << 16 | b); } } } return dst; } private static BufferedImage disparity( GrayF32 src, @Nullable BufferedImage dst, int range, int invalidColor ) { dst = ConvertBufferedImage.checkDeclare(src.width, src.height, dst, BufferedImage.TYPE_INT_RGB); 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/range); b = (int)(255*(range - v)/range); } dst.setRGB(x, y, r << 16 | b); } } } return dst; } /** * Colorizes an inverse depth image. * * @param src Inverse depth image * @param dst Output rendering * @param maxValue Max possible value. Reminder that larger means closer * @param invalidColor What color to mark pixels with no depth info * @return rendered image */ public static BufferedImage inverseDepth( GrayF32 src, @Nullable BufferedImage dst, float maxValue, int invalidColor ) { return inverseDepth(src, dst, 0.0f, maxValue, invalidColor); } /** * Colorizes an inverse depth image. * * @param src Inverse depth image * @param dst Output rendering * @param minValue Minimum possible value. Reminder that larger means closer. If < 0.0f it will auto select * @param maxValue Max possible value. Reminder that larger means closer. If <= 0.0f it will auto select * @param invalidColor What color to mark pixels with no depth info * @return rendered image */ public static BufferedImage inverseDepth( GrayF32 src, @Nullable BufferedImage dst, float minValue, float maxValue, int invalidColor ) { int bufferedType = dst == null ? BufferedImage.TYPE_INT_RGB : dst.getType(); dst = ConvertBufferedImage.checkDeclare(src.width, src.height, dst, bufferedType); // Find max value ignoring NaN if (maxValue <= 0.0f) { maxValue = ImageStatistics.max(src); } // Find the smallest valid value that's not at infinity if (minValue < 0.0f) { minValue = maxValue; for (int y = 0; y < src.height; y++) { int index = src.getIndex(0, y); int end = index + src.width; while (index < end) { float v = src.data[index++]; if (v > 0.0f && v < minValue) minValue = v; } } } float range = maxValue - minValue; // Avoid divide by zero if (range == 0.0f) range = 1f; PixelToRgb colorizer = createInverseDepthColorizer(src, minValue, maxValue, invalidColor, range); DataBuffer buffer = dst.getRaster().getDataBuffer(); if (buffer.getDataType() == DataBuffer.TYPE_BYTE && isKnownByteFormat(dst)) { applyTo(colorizer, src.width, src.height, (DataBufferByte)buffer, dst.getRaster()); } else if (buffer.getDataType() == DataBuffer.TYPE_INT) { applyTo(colorizer, src.width, src.height, (DataBufferInt)buffer, dst.getRaster()); } else { for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { dst.setRGB(x, y, colorizer.getRgb(x, y)); } } } // hack so that it knows the buffer has been modified dst.setRGB(0, 0, dst.getRGB(0, 0)); return dst; } private static PixelToRgb createInverseDepthColorizer( GrayF32 src, float minValue, float maxValue, int invalidColor, float range ) { return ( x, y ) -> { float v = src.unsafe_get(x, y); if (v < minValue) { return invalidColor; } else if (v == 0.0f) { return 0; } else { v = Math.min(v, maxValue) - minValue; int r = (int)(255*v/range); int b = (int)(255*(range - v)/range); return r << 16 | b; } }; } private static void colorizeSign( GrayF32 src, BufferedImage dst, float maxAbsValue ) { DataBuffer buffer = dst.getRaster().getDataBuffer(); if (buffer.getDataType() == DataBuffer.TYPE_INT) { colorizeSign(src, (DataBufferInt)buffer, maxAbsValue); } else { 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 (Float.isNaN(v)) { rgb = 0x000088; } else if (v > 0) { rgb = (int)(255*v/maxAbsValue) << 16; } else { rgb = (int)(-255*v/maxAbsValue) << 8; } dst.setRGB(x, y, rgb); } } } } private static void colorizeSign( GrayF32 src, DataBufferInt buffer, float maxAbsValue ) { final float[] srcData = src.data; final int[] dstData = buffer.getData(); //CONCURRENT_BELOW BoofConcurrency.loopFor(0, src.height, y -> { for (int y = 0; y < src.height; y++) { int indexSrc = src.startIndex + y*src.stride; int indexDst = y*src.width; for (int x = 0; x < src.width; x++) { float v = srcData[indexSrc++]; int rgb; if (Float.isNaN(v)) { rgb = 0x000088; } else if (v > 0) { rgb = (int)(255*v/maxAbsValue) << 16; } else { rgb = (int)(-255*v/maxAbsValue) << 8; } dstData[indexDst++] = 0xFF << 24 | rgb; } } //CONCURRENT_ABOVE }); } public static BufferedImage graySign( GrayF32 src, @Nullable 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 void grayMagnitude( GrayF32 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); } } } private static void grayMagnitude( GrayF64 src, BufferedImage dst, double maxAbsValue ) { for (int y = 0; y < src.height; y++) { for (int x = 0; x < src.width; x++) { double v = Math.abs(src.get(x, y)); int rgb = (int)(255*v/maxAbsValue); dst.setRGB(x, y, rgb << 16 | rgb << 8 | rgb); } } } /** * If null the dst is declared, otherwise it checks to see if the 'dst' as the same shape as 'src'. * * The returned image will be 8-bit RGB */ private static BufferedImage checkInputs( ImageBase src, @Nullable BufferedImage dst ) { if (dst != null) { if (dst.getWidth() != src.getWidth() || dst.getHeight() != src.getHeight()) { dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB); // throw new IllegalArgumentException("image dimension are different. src=" // +src.width+"x"+src.height+" dst="+dst.getWidth()+"x"+dst.getHeight()); } } else { dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB); } return dst; } /** * Renders two gradients on the same image using two sets of colors, on for each input image. * * @param derivX (Input) Image with positive and negative values. * @param derivY (Input) Image with positive and negative values. * @param maxAbsValue The largest absolute value of any pixel in the image. Set to < 0 if not known. * @return visualized gradient */ public static BufferedImage colorizeGradient( ImageGray derivX, ImageGray derivY, double maxAbsValue, BufferedImage output ) { if (derivX instanceof GrayS16) { return colorizeGradient((GrayS16)derivX, (GrayS16)derivY, (int)maxAbsValue, output); } else if (derivX instanceof GrayF32) { return colorizeGradient((GrayF32)derivX, (GrayF32)derivY, (float)maxAbsValue, output); } else { throw new IllegalArgumentException("Image type not supported"); } } /** * Renders two gradients on the same image using two sets of colors, on for each input image. * * @param derivX (Input) Image with positive and negative values. * @param derivY (Input) Image with positive and negative values. * @param maxAbsValue The largest absolute value of any pixel in the image. Set to < 0 if not known. * @return visualized gradient */ public static BufferedImage colorizeGradient( GrayS16 derivX, GrayS16 derivY, int maxAbsValue, @Nullable BufferedImage output ) { InputSanityCheck.checkSameShape(derivX, derivY); if (output == null) output = new BufferedImage(derivX.width, derivX.height, BufferedImage.TYPE_INT_RGB); WritableRaster raster = output.getRaster(); DataBufferInt buffer = (DataBufferInt)raster.getDataBuffer(); int[] outData = buffer.getData(); int outOffset = ConvertRaster.getOffset(raster); if (maxAbsValue < 0) { maxAbsValue = ImageStatistics.maxAbs(derivX); maxAbsValue = Math.max(maxAbsValue, ImageStatistics.maxAbs(derivY)); } if (maxAbsValue == 0) return output; int indexOut = outOffset; for (int y = 0; y < derivX.height; y++) { int indexX = derivX.startIndex + y*derivX.stride; int indexY = derivY.startIndex + y*derivY.stride; for (int x = 0; x < derivX.width; x++) { int valueX = derivX.data[indexX++]; int valueY = derivY.data[indexY++]; int r = 0, g = 0, b = 0; if (valueX > 0) { r = 255*valueX/maxAbsValue; } else { g = -255*valueX/maxAbsValue; } if (valueY > 0) { b = 255*valueY/maxAbsValue; } else { int v = -255*valueY/maxAbsValue; r += v; g += v; if (r > 255) r = 255; if (g > 255) g = 255; } outData[indexOut++] = r << 16 | g << 8 | b; } } return output; } /** * Renders two gradients on the same image using two sets of colors, on for each input image. * * @param derivX (Input) Image with positive and negative values. * @param derivY (Input) Image with positive and negative values. * @param maxAbsValue The largest absolute value of any pixel in the image. Set to < 0 if not known. * @return visualized gradient */ public static BufferedImage colorizeGradient( GrayF32 derivX, GrayF32 derivY, float maxAbsValue, @Nullable BufferedImage output ) { InputSanityCheck.checkSameShape(derivX, derivY); output = ConvertBufferedImage.checkDeclare(derivX.width, derivX.height, output, BufferedImage.TYPE_INT_RGB); WritableRaster raster = output.getRaster(); DataBufferInt buffer = (DataBufferInt)raster.getDataBuffer(); int[] outData = buffer.getData(); int outOffset = ConvertRaster.getOffset(raster); if (maxAbsValue < 0) { maxAbsValue = ImageStatistics.maxAbs(derivX); maxAbsValue = Math.max(maxAbsValue, ImageStatistics.maxAbs(derivY)); } if (maxAbsValue == 0) return output; int indexOut = outOffset; for (int y = 0; y < derivX.height; y++) { int indexX = derivX.startIndex + y*derivX.stride; int indexY = derivY.startIndex + y*derivY.stride; for (int x = 0; x < derivX.width; x++) { float valueX = derivX.data[indexX++]; float valueY = derivY.data[indexY++]; int r = 0, g = 0, b = 0; if (valueX > 0) { r = (int)(255*valueX/maxAbsValue); } else { g = -(int)(255*valueX/maxAbsValue); } if (valueY > 0) { b = (int)(255*valueY/maxAbsValue); } else { int v = -(int)(255*valueY/maxAbsValue); r += v; g += v; if (r > 255) r = 255; if (g > 255) g = 255; } outData[indexOut++] = r << 16 | g << 8 | b; } } return output; } private static void applyTo( PixelToRgb src, int width, int height, DataBufferByte buffer, WritableRaster dst ) { final byte[] dstData = buffer.getData(); final int numBands = dst.getNumBands(); if (numBands == 1) { //CONCURRENT_BELOW BoofConcurrency.loopFor(0, src.height, y -> { for (int y = 0; y < height; y++) { int indexDst = y*width*numBands; for (int x = 0; x < width; x++) { int rgb = src.getRgb(x, y); int band0 = rgb & 0xFF; int band1 = (rgb >> 8) & 0xFF; int band2 = (rgb >> 16) & 0xFF; dstData[indexDst++] = (byte)((band0 + band1 + band2)/3); } } //CONCURRENT_ABOVE }); } else if (numBands == 3) { //CONCURRENT_BELOW BoofConcurrency.loopFor(0, src.height, y -> { for (int y = 0; y < height; y++) { int indexDst = y*width*numBands; for (int x = 0; x < width; x++) { int rgb = src.getRgb(x, y); dstData[indexDst++] = (byte)(rgb & 0xFF); dstData[indexDst++] = (byte)((rgb >> 8) & 0xFF); dstData[indexDst++] = (byte)((rgb >> 16) & 0xFF); } } //CONCURRENT_ABOVE }); } else { //CONCURRENT_BELOW BoofConcurrency.loopFor(0, src.height, y -> { for (int y = 0; y < height; y++) { int indexDst = y*width*numBands; for (int x = 0; x < width; x++) { int rgb = src.getRgb(x, y); dstData[indexDst++] = (byte)(rgb & 0xFF); dstData[indexDst++] = (byte)((rgb >> 8) & 0xFF); dstData[indexDst++] = (byte)((rgb >> 16) & 0xFF); dstData[indexDst++] = (byte)0xFF; } } //CONCURRENT_ABOVE }); } } private static void applyTo( PixelToRgb src, int width, int height, DataBufferInt buffer, WritableRaster dst ) { final int[] dstData = buffer.getData(); //CONCURRENT_BELOW BoofConcurrency.loopFor(0, src.height, y -> { for (int y = 0; y < height; y++) { int indexDst = y*width; for (int x = 0; x < width; x++) { dstData[indexDst++] = src.getRgb(x, y) | 0xFF000000; } } //CONCURRENT_ABOVE }); } @FunctionalInterface interface PixelToRgb { int getRgb( int x, int y ); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy