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

jaitools.media.jai.maskedconvolve.MaskedConvolveOpImage Maven / Gradle / Ivy

Go to download

Provides a single jar containing all JAI-tools modules which you can use instead of including individual modules in your project. Note: It does not include the Jiffle scripting language or Jiffle image operator.

The newest version!
/*
 * Copyright 2009 Michael Bedward
 * 
 * This file is part of jai-tools.

 * jai-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.

 * jai-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public 
 * License along with jai-tools.  If not, see .
 * 
 */

package jaitools.media.jai.maskedconvolve;

import java.awt.Rectangle;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.lang.reflect.Constructor;
import java.util.Map;
import javax.media.jai.AreaOpImage;
import javax.media.jai.BorderExtender;
import javax.media.jai.ImageLayout;
import javax.media.jai.KernelJAI;
import javax.media.jai.ROI;
import javax.media.jai.RasterAccessor;
import javax.media.jai.RasterFormatTag;

/**
 * An operator to perform masked convolution on a source image.
 * @see MaskedConvolveDescriptor Description of the algorithm and example
 * 
 * @author Michael Bedward
 * @since 1.0
 * @version $Id: MaskedConvolveOpImage.java 1383 2011-02-10 11:22:29Z michael.bedward $
 */
public class MaskedConvolveOpImage extends AreaOpImage {

    /* Source image variables */
    private int[] srcBandOffsets;
    private int srcPixelStride;
    private int srcScanlineStride;

    /* Destination image variables */
    private int destWidth;
    private int destHeight;
    private int destBands;
    private int[] dstBandOffsets;
    private int dstPixelStride;
    private int dstScanlineStride;
    
    /* Kernel variables. */
    private float[] kernelData;
    private int kernelW,  kernelH;
    private int kernelKeyX, kernelKeyY;

    /* Records which kernel cells have non-zero values */
    private boolean[] kernelActive;

    /**
     * The tolerance used when marking kernel cells as active
     * (ie. having a non-zero value that contributes to the
     * convolution result) or inactive. Cells with an absolute
     * value less than this tolerance will be treated as
     * inactive (zero).
     */
    public final static float KERNEL_TOL = 1.0e-6F;

    /* ROI and options */
    private ROI roi;
    private boolean maskSrc;
    private boolean maskDest;

    /*
     * The value to write to the destination when there is no
     * convolution result
     */
    Number nilValueNumber;

    /*
     * The minimum number of non-zero kernel cells that must overlap
     * unmasked source image cells for a convolution result to be written
     * to the destination image
     */
    private int minKernelCells;

    /**
     * Constructor
     * @param source a RenderedImage.
     * @param extender a BorderExtender, or null.
     * @param config configurable attributes of the image (see {@link AreaOpImage})
     * @param layout an ImageLayout optionally containing the tile grid layout,
     *        SampleModel, and ColorModel, or null.
     * @param kernel the convolution kernel
     * @param roi the ROI used to control masking; must contain the source image bounds
     * @param maskSrc if true, exclude masked pixels ({@code roi.contains == false}) from
     * convolution kernel calculation
     * @param maskDest if true, do not place kernel over masked pixels (dest will be 0)
     * @throws IllegalArgumentException if the roi's bounds do not contain the entire
     * source image
     * @see MaskedConvolveDescriptor
     */
    public MaskedConvolveOpImage(RenderedImage source,
            BorderExtender extender,
            Map config,
            ImageLayout layout,
            KernelJAI kernel,
            ROI roi,
            Boolean maskSrc,
            Boolean maskDest,
            Number nilValue,
            int minCells) {
        
        super(source,
                layout,
                config,
                true,
                extender,
                kernel.getLeftPadding(),
                kernel.getRightPadding(),
                kernel.getTopPadding(),
                kernel.getBottomPadding());

        Rectangle sourceBounds = new Rectangle(
                source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight());
        
        if (!roi.getBounds().contains(sourceBounds)) {
            throw new IllegalArgumentException("The bounds of the ROI must contain the source image");
        }

        this.roi = roi;
        this.maskSrc = maskSrc.booleanValue();
        this.maskDest = maskDest.booleanValue();

        /*
         * We defensivley copy the Number object, but have to
         * resort to reflection to do so :-(
         */
        try {
            Constructor ctor = nilValue.getClass().getConstructor(String.class);
            this.nilValueNumber = ctor.newInstance(nilValue.toString());
        } catch (Exception ex) {
            throw new IllegalStateException("Problem copying nilValue arg", ex);
        }

        kernelData = kernel.getKernelData();
        kernelActive = new boolean[kernelData.length];
        for (int i = 0; i < kernelData.length; i++) {
            if (Math.abs(kernelData[i]) > KERNEL_TOL) {
                kernelActive[i] = true;
            } else {
                kernelActive[i] = false;
            }
        }

        kernelW = kernel.getWidth();
        kernelH = kernel.getHeight();
        kernelKeyX = kernel.getXOrigin();
        kernelKeyY = kernel.getYOrigin();

        minKernelCells = minCells;
    }

    /**
     * Performs convolution on a specified rectangle. 
     *
     * @param sources an array of source Rasters, guaranteed to provide all
     *        necessary source data for computing the output.
     * @param dest a WritableRaster tile containing the area to be computed.
     * @param destRect the rectangle within dest to be processed.
     */
    @Override
    protected void computeRect(Raster[] sources,
            WritableRaster dest,
            Rectangle destRect) {

        RasterFormatTag[] formatTags = getFormatTags();

        Raster source = sources[0];
        Rectangle srcRect = mapDestRect(destRect, 0);


        RasterAccessor srcAcc =
                new RasterAccessor(source, srcRect,
                formatTags[0], getSourceImage(0).getColorModel());
        
        RasterAccessor destAcc =
                new RasterAccessor(dest, destRect,
                formatTags[1], getColorModel());
        
        convolve(srcAcc, destAcc);
    }

    /**
     * Initialize common variables then delegate the convolution to
     * one of the data-type-specific methods
     * 
     * @param srcAcc source raster accessor
     * @param destAcc dest raster accessor
     */
    private void convolve(RasterAccessor srcAcc, RasterAccessor destAcc) {
        destWidth = destAcc.getWidth();
        destHeight = destAcc.getHeight();
        destBands = destAcc.getNumBands();


        dstBandOffsets = destAcc.getBandOffsets();
        dstPixelStride = destAcc.getPixelStride();
        dstScanlineStride = destAcc.getScanlineStride();

        srcBandOffsets = srcAcc.getBandOffsets();
        srcPixelStride = srcAcc.getPixelStride();
        srcScanlineStride = srcAcc.getScanlineStride();

        switch (destAcc.getDataType()) {
            case DataBuffer.TYPE_BYTE:
                convolveAsByteData(srcAcc, destAcc);
                break;
            case DataBuffer.TYPE_INT:
                convolveAsIntData(srcAcc, destAcc);
                break;
            case DataBuffer.TYPE_SHORT:
                convolveAsShortData(srcAcc, destAcc);
                break;
            case DataBuffer.TYPE_USHORT:
                convolveAsUShortData(srcAcc, destAcc);
                break;
            case DataBuffer.TYPE_FLOAT:
                convolveAsFloatData(srcAcc, destAcc);
                break;
            case DataBuffer.TYPE_DOUBLE:
                convolveAsDoubleData(srcAcc, destAcc);
                break;
        }

        if (destAcc.isDataCopy()) {
            destAcc.clampDataArrays();
            destAcc.copyDataToRaster();
        }
    }

    private void convolveAsByteData(RasterAccessor srcAcc, RasterAccessor destAcc) {

        byte srcData[][] = srcAcc.getByteDataArrays();
        byte destData[][] = destAcc.getByteDataArrays();

        int nilValue = nilValueNumber.intValue();
        
        for (int k = 0; k < destBands; k++) {
            int destY = destAcc.getY();
            byte destBandData[] = destData[k];
            byte srcBandDat[] = srcData[k];
            int srcScanlineOffset = srcBandOffsets[k];
            int dstScanlineOffset = dstBandOffsets[k];
            for (int j = 0; j < destHeight; j++, destY++) {
                int destX = destAcc.getX();
                int srcPixelOffset = srcScanlineOffset;
                int dstPixelOffset = dstScanlineOffset;

                for (int i = 0; i < destWidth; i++, destX++) {
                    int val = 0;
                    int count = 0;
                    if (!maskDest || roi.contains(destX, destY)) {
                        int srcY = destY - kernelKeyY;
                        float fval = 0.5F;
                        int kernelOffset = 0;
                        int imageVerticalOffset = srcPixelOffset;
                        for (int u = 0; u < kernelH; u++, srcY++) {
                            int srcX = destX - kernelKeyX;
                            int imageOffset = imageVerticalOffset;
                            for (int v = 0; v < kernelW; v++, srcX++) {
                                if (kernelActive[kernelOffset + v]) {
                                    if (!maskSrc || roi.contains(srcX, srcY)) {
                                        fval += ((int) srcBandDat[imageOffset] & 0xff) * kernelData[kernelOffset + v];
                                        count++;
                                    }
                                }
                                imageOffset += srcPixelStride;
                            }
                            kernelOffset += kernelW;
                            imageVerticalOffset += srcScanlineStride;
                        }

                        if (count >= minKernelCells) {
                            val = (int) fval;
                        } else {
                            val = nilValue;
                        }

                        if (val < 0) {
                            val = 0;
                        } else if (val > 255) {
                            val = 255;
                        }
                    }
                    
                    destBandData[dstPixelOffset] = (byte) val;
                    srcPixelOffset += srcPixelStride;
                    dstPixelOffset += dstPixelStride;
                }
                srcScanlineOffset += srcScanlineStride;
                dstScanlineOffset += dstScanlineStride;
            }
        }
    }

    private void convolveAsShortData(RasterAccessor srcAcc, RasterAccessor destAcc) {

        short destData[][] = destAcc.getShortDataArrays();
        short srcData[][] = srcAcc.getShortDataArrays();

        int nilValue = nilValueNumber.intValue();

        for (int k = 0; k < destBands; k++) {
            int y = destAcc.getY();
            short destBand[] = destData[k];
            short srcBand[] = srcData[k];
            int srcScanlineOffset = srcBandOffsets[k];
            int dstScanlineOffset = dstBandOffsets[k];
            for (int j = 0; j < destHeight; j++, y++) {
                int x = destAcc.getX();
                int srcPixelOffset = srcScanlineOffset;
                int dstPixelOffset = dstScanlineOffset;

                for (int i = 0; i < destWidth; i++, x++) {
                    int val = 0;
                    int count = 0;
                    if (!maskDest || roi.contains(x, y)) {
                        int srcY = y - kernelKeyY;
                        float fval = 0.5F;
                        int kernelOffset = 0;
                        int imageVerticalOffset = srcPixelOffset;
                        for (int u = 0; u < kernelH; u++, srcY++) {
                            int srcX = x - kernelKeyX;
                            int imageOffset = imageVerticalOffset;
                            for (int v = 0; v < kernelW; v++, srcX++) {
                                if (kernelActive[kernelOffset + v]) {
                                    if (!maskSrc || roi.contains(srcX, srcY)) {
                                        fval += srcBand[imageOffset] * kernelData[kernelOffset + v];
                                        count++ ;
                                    }
                                }
                                imageOffset += srcPixelStride;
                            }
                            kernelOffset += kernelW;
                            imageVerticalOffset += srcScanlineStride;
                        }

                        if (count >= minKernelCells) {
                            val = (int) fval;
                        } else {
                            val = nilValue;
                        }

                        if (val < Short.MIN_VALUE) {
                            val = Short.MIN_VALUE;
                        } else if (val > Short.MAX_VALUE) {
                            val = Short.MAX_VALUE;
                        }
                    }
                    destBand[dstPixelOffset] = (short) val;
                    srcPixelOffset += srcPixelStride;
                    dstPixelOffset += dstPixelStride;
                }
                srcScanlineOffset += srcScanlineStride;
                dstScanlineOffset += dstScanlineStride;
            }
        }
    }

    private void convolveAsUShortData(RasterAccessor srcAcc, RasterAccessor destAcc) {

        short destData[][] = destAcc.getShortDataArrays();
        short srcData[][] = srcAcc.getShortDataArrays();

        int nilValue = nilValueNumber.intValue();

        for (int k = 0; k < destBands; k++) {
            int y = destAcc.getY();
            short destBand[] = destData[k];
            short srcBand[] = srcData[k];
            int srcScanlineOffset = srcBandOffsets[k];
            int dstScanlineOffset = dstBandOffsets[k];
            for (int j = 0; j < destHeight; j++, y++) {
                int x = destAcc.getX();
                int srcPixelOffset = srcScanlineOffset;
                int dstPixelOffset = dstScanlineOffset;

                for (int i = 0; i < destWidth; i++, x++) {
                    int val = 0;
                    int count = 0;
                    if (!maskDest || roi.contains(x, y)) {
                        int srcY = y - kernelKeyY;
                        float fval = 0.5F;
                        int kernelOffset = 0;
                        int imageVerticalOffset = srcPixelOffset;
                        for (int u = 0; u < kernelH; u++, srcY++) {
                            int srcX = x - kernelKeyX;
                            int imageOffset = imageVerticalOffset;
                            for (int v = 0; v < kernelW; v++, srcX++) {
                                if (kernelActive[kernelOffset + v]) {
                                    if (!maskSrc || roi.contains(srcX, srcY)) {
                                        fval += (srcBand[imageOffset] & 0xffff) * kernelData[kernelOffset + v];
                                        count++ ;
                                    }
                                }
                                imageOffset += srcPixelStride;
                            }
                            kernelOffset += kernelW;
                            imageVerticalOffset += srcScanlineStride;
                        }

                        if (count >= minKernelCells) {
                            val = (int) fval;
                        } else {
                            val = nilValue;
                        }

                        if (val < 0) {
                            val = 0;
                        } else if (val > 0xffff) {
                            val = 0xffff;
                        }
                    }
                    destBand[dstPixelOffset] = (short) val;
                    srcPixelOffset += srcPixelStride;
                    dstPixelOffset += dstPixelStride;
                }
                srcScanlineOffset += srcScanlineStride;
                dstScanlineOffset += dstScanlineStride;
            }
        }
    }

    private void convolveAsIntData(RasterAccessor srcAcc, RasterAccessor destAcc) {

        int destData[][] = destAcc.getIntDataArrays();
        int srcData[][] = srcAcc.getIntDataArrays();

        int nilValue = nilValueNumber.intValue();

        for (int k = 0; k < destBands; k++) {
            int y = destAcc.getY();
            int destBand[] = destData[k];
            int srcBand[] = srcData[k];
            int srcScanlineOffset = srcBandOffsets[k];
            int dstScanlineOffset = dstBandOffsets[k];
            for (int j = 0; j < destHeight; j++, y++) {
                int x = destAcc.getX();
                int srcPixelOffset = srcScanlineOffset;
                int dstPixelOffset = dstScanlineOffset;

                for (int i = 0; i < destWidth; i++, x++) {
                    float fval = 0.5F;
                    int count = 0;
                    if (!maskDest || roi.contains(x, y)) {
                        int srcY = y - kernelKeyY;
                        int kernelOffset = 0;
                        int imageVerticalOffset = srcPixelOffset;
                        for (int u = 0; u < kernelH; u++, srcY++) {
                            int srcX = x - kernelKeyX;
                            int imageOffset = imageVerticalOffset;
                            for (int v = 0; v < kernelW; v++, srcX++) {
                                if (kernelActive[kernelOffset + v]) {
                                    if (!maskSrc || roi.contains(srcX, srcY)) {
                                        fval += (srcBand[imageOffset]) * kernelData[kernelOffset + v];
                                        count++ ;
                                    }
                                }
                                imageOffset += srcPixelStride;
                            }
                            kernelOffset += kernelW;
                            imageVerticalOffset += srcScanlineStride;
                        }
                    }

                    if (count >= minKernelCells) {
                        destBand[dstPixelOffset] = (int) fval;
                    } else {
                        destBand[dstPixelOffset] = nilValue;
                    }

                    srcPixelOffset += srcPixelStride;
                    dstPixelOffset += dstPixelStride;
                }
                srcScanlineOffset += srcScanlineStride;
                dstScanlineOffset += dstScanlineStride;
            }
        }
    }

    private void convolveAsFloatData(RasterAccessor srcAcc, RasterAccessor destAcc) {

        float destData[][] = destAcc.getFloatDataArrays();
        float srcData[][] = srcAcc.getFloatDataArrays();

        float nilValue = nilValueNumber.floatValue();

        for (int k = 0; k < destBands; k++) {
            int y = destAcc.getY();
            float destBand[] = destData[k];
            float srcBand[] = srcData[k];
            int srcScanlineOffset = srcBandOffsets[k];
            int dstScanlineOffset = dstBandOffsets[k];
            for (int j = 0; j < destHeight; j++, y++) {
                int x = destAcc.getX();
                int srcPixelOffset = srcScanlineOffset;
                int dstPixelOffset = dstScanlineOffset;

                for (int i = 0; i < destWidth; i++, x++) {
                    float fval = 0.0F;
                    int count = 0;
                    if (!maskDest || roi.contains(x, y)) {
                        int srcY = y - kernelKeyY;
                        int kernelOffset = 0;
                        int imageVerticalOffset = srcPixelOffset;
                        for (int u = 0; u < kernelH; u++, srcY++) {
                            int srcX = x - kernelKeyX;
                            int imageOffset = imageVerticalOffset;
                            for (int v = 0; v < kernelW; v++, srcX++) {
                                if (kernelActive[kernelOffset + v]) {
                                    if (!maskSrc || roi.contains(srcX, srcY)) {
                                        fval += (srcBand[imageOffset]) * kernelData[kernelOffset + v];
                                        count++ ;
                                    }
                                }
                                imageOffset += srcPixelStride;
                            }
                            kernelOffset += kernelW;
                            imageVerticalOffset += srcScanlineStride;
                        }
                    }

                    if (count >= minKernelCells) {
                        destBand[dstPixelOffset] = fval;
                    } else {
                        destBand[dstPixelOffset] = nilValue;
                    }

                    srcPixelOffset += srcPixelStride;
                    dstPixelOffset += dstPixelStride;
                }
                srcScanlineOffset += srcScanlineStride;
                dstScanlineOffset += dstScanlineStride;
            }
        }
    }

    private void convolveAsDoubleData(RasterAccessor srcAcc, RasterAccessor destAcc) {

        double destData[][] = destAcc.getDoubleDataArrays();
        double srcData[][] = srcAcc.getDoubleDataArrays();

        double nilValue = nilValueNumber.doubleValue();

        for (int k = 0; k < destBands; k++) {
            int y = destAcc.getY();
            double destBand[] = destData[k];
            double srcBand[] = srcData[k];
            int srcScanlineOffset = srcBandOffsets[k];
            int dstScanlineOffset = dstBandOffsets[k];
            for (int j = 0; j < destHeight; j++, y++) {
                int x = destAcc.getX();
                int srcPixelOffset = srcScanlineOffset;
                int dstPixelOffset = dstScanlineOffset;

                for (int i = 0; i < destWidth; i++, x++) {
                    double dval = 0.0D;
                    int count = 0;
                    if (!maskDest || roi.contains(x, y)) {
                        int srcY = y - kernelKeyY;
                        int kernelOffset = 0;
                        int imageVerticalOffset = srcPixelOffset;
                        for (int u = 0; u < kernelH; u++, srcY++) {
                            int srcX = x - kernelKeyX;
                            int imageOffset = imageVerticalOffset;
                            for (int v = 0; v < kernelW; v++, srcX++) {
                                if (kernelActive[kernelOffset + v]) {
                                    if (!maskSrc || roi.contains(srcX, srcY)) {
                                        dval += (srcBand[imageOffset]) * kernelData[kernelOffset + v];
                                        count++;
                                    }
                                }
                                imageOffset += srcPixelStride;
                            }
                            kernelOffset += kernelW;
                            imageVerticalOffset += srcScanlineStride;
                        }
                    }

                    if (count >= minKernelCells) {
                        destBand[dstPixelOffset] = dval;
                    } else {
                        destBand[dstPixelOffset] = nilValue;
                    }

                    srcPixelOffset += srcPixelStride;
                    dstPixelOffset += dstPixelStride;
                }
                dstScanlineOffset += dstScanlineStride;
                srcScanlineOffset += srcScanlineStride;
            }
        }
    }    
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy