boofcv.alg.transform.wavelet.UtilWavelet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of boofcv-ip Show documentation
Show all versions of boofcv-ip Show documentation
BoofCV is an open source Java library for real-time computer vision and robotics applications.
/*
* Copyright (c) 2021, 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.alg.transform.wavelet;
import boofcv.core.image.border.BorderIndex1D_Reflect;
import boofcv.core.image.border.BorderIndex1D_Wrap;
import boofcv.struct.border.BorderIndex1D;
import boofcv.struct.border.BorderType;
import boofcv.struct.image.*;
import boofcv.struct.wavelet.WlBorderCoef;
import boofcv.struct.wavelet.WlCoef;
/**
* Various functions which are useful when working with or computing wavelet transforms.
*
* @author Peter Abeles
*/
public class UtilWavelet {
/**
* The original image can have an even or odd number of width/height. While the transformed
* image must have an even number of pixels. If the original image is even then the sames
* are the same, otherwise the transformed image's shape is rounded up.
*
* @param original Original input image.
* @param transformed Image which has been transformed.
*/
public static void checkShape(ImageGray original , ImageGray transformed )
{
if( transformed.width % 2 == 1 || transformed.height % 2 == 1 )
throw new IllegalArgumentException("Image containing the wavelet transform must have an even width and height.");
int w = original.width + original.width%2;
int h = original.height + original.height%2;
if( transformed.width < w || transformed.height < h)
throw new IllegalArgumentException("Transformed image must be larger than the original image. " +
"("+w+","+h+") vs ("+transformed.width+","+transformed.height+")");
}
public static void checkShape(WlCoef desc , ImageGray original , ImageGray transformed , int level )
{
ImageDimension tranDim = UtilWavelet.transformDimension(original,level);
if( transformed.width != tranDim.width || transformed.height != tranDim.height ) {
throw new IllegalArgumentException("Image containing the wavelet transform must be "+tranDim.width+" x "+tranDim.height);
}
if( original.width < desc.getScalingLength() || original.height < desc.getScalingLength() )
throw new IllegalArgumentException("Original image's width and height must be large enough the number of scaling coefficients.");
if( original.width < desc.getWaveletLength() || original.height < desc.getWaveletLength() )
throw new IllegalArgumentException("Original image's width and height must be large enough the number of wavelet coefficients.");
}
public static int computeScale( int level ) {
if( level <= 1 )
return 1;
return (int)Math.pow(2,level-1);
}
/**
* Returns the number that the output image needs to be divisible by.
*/
public static int computeDiv( int level ) {
if( level <= 1 )
return 2;
return (int)Math.pow(2,level-1);
}
/**
* Returns dimension which is required for the transformed image in a multilevel
* wavelet transform.
*/
public static ImageDimension transformDimension( ImageBase orig , int level )
{
return transformDimension(orig.width,orig.height,level);
}
public static ImageDimension transformDimension( int width , int height , int level )
{
int div = computeDiv(level);
int w = width%div;
int h = height%div;
width += w > 0 ? div-w : 0;
height += h > 0 ? div-h : 0;
return new ImageDimension(width,height);
}
/**
*
* Compute the energy of the specified array.
*
*
*
* E = sum( i=1..N , a[i]*a[i] )
*
*/
public static double computeEnergy( float []array ) {
double total = 0;
for( int i = 0; i < array.length; i++ ) {
total += array[i]*array[i];
}
return total;
}
/**
*
* Compute the energy of the specified array.
*
*
*
* E = sum( i=1..N , a[i]*a[i] ) / (N*d*d)
*
*/
public static double computeEnergy( int []array , int denominator) {
double total = 0;
for( int i = 0; i < array.length; i++ ) {
total += array[i]*array[i];
}
total /= denominator*denominator;
return total;
}
public static double sumCoefficients( float []array ) {
double total = 0;
for( int i = 0; i < array.length; i++ ) {
total += array[i];
}
return total;
}
public static int sumCoefficients( int []array ) {
int total = 0;
for( int i = 0; i < array.length; i++ ) {
total += array[i];
}
return total;
}
/**
* Returns the lower border for a forward wavelet transform.
*/
public static int borderForwardLower( WlCoef desc ) {
int ret = -Math.min(desc.offsetScaling,desc.offsetWavelet);
return ret + (ret % 2);
}
/**
* Returns the upper border (offset from image edge) for a forward wavelet transform.
*/
public static int borderForwardUpper( WlCoef desc , int dataLength) {
int w = Math.max( desc.offsetScaling+desc.getScalingLength() , desc.offsetWavelet+desc.getWaveletLength());
int a = dataLength%2;
w -= a;
return Math.max((w + (w%2))-2,0)+a;
}
/**
* Returns the lower border for an inverse wavelet transform.
*/
public static int borderInverseLower( WlBorderCoef desc, BorderIndex1D border ) {
WlCoef inner = desc.getInnerCoefficients();
int borderSize = borderForwardLower(inner);
WlCoef ll = borderSize > 0 ? inner : null;
WlCoef lu = ll;
WlCoef uu = inner;
int indexLU = 0;
if( desc.getLowerLength() > 0 ) {
ll = desc.getBorderCoefficients(0);
indexLU = desc.getLowerLength()*2-2;
lu = desc.getBorderCoefficients(indexLU);
}
if( desc.getUpperLength() > 0 ) {
uu = desc.getBorderCoefficients(-2);
}
border.setLength(2000);
borderSize = checkInverseLower(ll,0,border,borderSize);
borderSize = checkInverseLower(lu,indexLU,border,borderSize);
borderSize = checkInverseLower(uu,1998,border,borderSize);
return borderSize;
}
public static int checkInverseLower( WlCoef coef, int index , BorderIndex1D border , int current ) {
if( coef == null )
return current;
// how far up and down the coefficients go
int a = index + Math.max( coef.getScalingLength()+coef.offsetScaling,coef.getWaveletLength()+coef.offsetScaling);
int b = index + Math.min( coef.offsetScaling , coef.offsetWavelet ) -1;
// the above -1 is needed because the lower bound is becoming an upper bound.
// lower bounds are inclusive and upper bounds are exclusive
// take in account the border
a = border.getIndex(a);
b = border.getIndex(b);
if( a > 1000 ) a = -1;
if( b > 1000 ) b = -1;
a = Math.max(a,b);
a += a%2;
return Math.max(a,current);
}
/**
* Returns the upper border (offset from image edge) for an inverse wavelet transform.
*/
public static int borderInverseUpper( WlBorderCoef desc , BorderIndex1D border, int dataLength ) {
WlCoef inner = desc.getInnerCoefficients();
int borderSize = borderForwardUpper(inner,dataLength);
borderSize += borderSize%2;
WlCoef uu = borderSize > 0 ? inner : null;
WlCoef ul = uu;
WlCoef ll = inner;
int indexUL = 1998;
if( desc.getUpperLength() > 0 ) {
uu = desc.getBorderCoefficients(-2);
indexUL = 2000-desc.getUpperLength()*2;
ul = desc.getBorderCoefficients(2000-indexUL);
}
if( desc.getLowerLength() > 0 ) {
ll = desc.getBorderCoefficients(0);
}
border.setLength(2000);
borderSize = checkInverseUpper(uu,2000-borderSize,border,borderSize);
borderSize = checkInverseUpper(ul,indexUL,border,borderSize);
borderSize = checkInverseUpper(ll,0,border,borderSize);
return borderSize;
}
public static int checkInverseUpper( WlCoef coef, int index , BorderIndex1D border , int current ) {
if( coef == null )
return current;
// how far up and down the coefficients go
int a = index + Math.max( coef.getScalingLength()+coef.offsetScaling,coef.getWaveletLength()+coef.offsetScaling)-1;
int b = index + Math.min( coef.offsetScaling , coef.offsetWavelet );
// the plus 1 for 'a' is needed because the lower bound is inclusive not exclusive
// take in account the border
a = border.getIndex(a);
b = border.getIndex(b);
if( a < 1000 ) a = 10000;
if( b < 1000 ) b = 10000;
a = 2000-Math.min(a,b);
a += a%2;
return Math.max(a,current);
}
/**
* Specialized rounding for use with integer wavelet transform.
*
* return (top +- div2) / divisor;
*
* @param top Top part of the equation.
* @param div2 The divisor divided by two.
* @param divisor The divisor.
* @return
*/
public static int round( int top , int div2 , int divisor ) {
if( top > 0 )
return (top + div2)/divisor;
else
return (top - div2)/divisor;
}
public static BorderType convertToType( BorderIndex1D b ) {
if( b instanceof BorderIndex1D_Reflect) {
return BorderType.REFLECT;
} else if( b instanceof BorderIndex1D_Wrap) {
return BorderType.WRAP;
} else {
throw new RuntimeException("Unknown border type: "+b.getClass().getSimpleName());
}
}
/**
* Adjusts the values inside a wavelet transform to make it easier to view.
*
* @param transform
* @param numLevels Number of levels in the transform
*/
public static void adjustForDisplay(ImageGray transform , int numLevels , double valueRange ) {
if( transform instanceof GrayF32)
adjustForDisplay((GrayF32)transform,numLevels,(float)valueRange);
else
adjustForDisplay((GrayI)transform,numLevels,(int)valueRange);
}
private static void adjustForDisplay(GrayF32 transform , int numLevels , float valueRange ) {
int div = (int)Math.pow(2,numLevels);
int minX = 0;
int minY = 0;
while( div >= 1 ) {
int maxX = transform.width/div;
int maxY = transform.height/div;
float max = 0;
for( int y = 0; y < maxY; y++ ) {
for( int x = 0; x < maxX; x++ ) {
if( x >= minX || y >= minY ) {
float val = Math.abs(transform.data[ transform.getIndex(x,y) ]);
max = Math.max(val,max);
}
}
}
for( int y = 0; y < maxY; y++ ) {
for( int x = 0; x < maxX; x++ ) {
if( x >= minX || y >= minY ) {
transform.data[ transform.getIndex(x,y) ] *= valueRange/max;
}
}
}
minX = maxX;
minY = maxY;
div /= 2;
}
}
private static void adjustForDisplay(GrayI transform , int numLevels , int valueRange ) {
int div = (int)Math.pow(2,numLevels);
int minX = 0;
int minY = 0;
while( div >= 1 ) {
int maxX = transform.width/div;
int maxY = transform.height/div;
int max = 0;
for( int y = 0; y < maxY; y++ ) {
for( int x = 0; x < maxX; x++ ) {
if( x >= minX || y >= minY ) {
int val = Math.abs(transform.get(x,y));
max = Math.max(val,max);
}
}
}
for( int y = 0; y < maxY; y++ ) {
for( int x = 0; x < maxX; x++ ) {
if( x >= minX || y >= minY ) {
int val = transform.get(x,y);
transform.set( x,y,val * valueRange/max);
}
}
}
minX = maxX;
minY = maxY;
div /= 2;
}
}
}