Kaydet (Commit) 05363527 authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl

Separable Gaussian Blur and Unsharpen Mask filter.

Currently implemented Gaussian Blur filter uses a static matrix
which means that the filter can not be parameterized. The new
implementation of Gaussian Blur filter and accepts a blur radius
as a parameter so the user can change the strength of the blur.
For this the blur matrix is generated at each call. The new Blur
implementation reuses separable convolution from Lanzcos rescale.

For negative values of radius the Bitmap will be sharpened. For
this an Unsharpen Mask filter is used, which is actually a blurred
image substracted from the original image.
üst c02dd533
......@@ -183,6 +183,7 @@ private:
{
sal_uInt16 mnSepiaPercent;
sal_uInt8 mcSolarGreyThreshold;
double mnRadius;
MosaicTileSize maMosaicTileSize;
EmbossAngles maEmbossAngles;
......@@ -197,6 +198,10 @@ public:
meFilter( BMP_FILTER_SOLARIZE ), mnProgressStart( nProgressStart ), mnProgressEnd( nProgressEnd ),
mcSolarGreyThreshold( cSolarGreyThreshold ) {}
BmpFilterParam( double nRadius, sal_uLong nProgressStart = 0, sal_uLong nProgressEnd = 0 ) :
meFilter( BMP_FILTER_SMOOTH ), mnProgressStart( nProgressStart ), mnProgressEnd( nProgressEnd ),
mnRadius( cSolarGreyThreshold ) {}
BmpFilterParam( sal_uInt16 nSepiaPercent, sal_uLong nProgressStart = 0, sal_uLong nProgressEnd = 0 ) :
meFilter( BMP_FILTER_SEPIA ), mnProgressStart( nProgressStart ), mnProgressEnd( nProgressEnd ),
mnSepiaPercent( nSepiaPercent ) {}
......@@ -397,6 +402,10 @@ public:
SAL_DLLPRIVATE sal_Bool ImplMosaic( const BmpFilterParam* pFilterParam, const Link* pProgress );
SAL_DLLPRIVATE sal_Bool ImplPopArt( const BmpFilterParam* pFilterParam, const Link* pProgress );
SAL_DLLPRIVATE bool ImplSeparableBlurFilter( const double aRadius = 0.7 );
SAL_DLLPRIVATE bool ImplSeparableUnsharpenFilter( const double aRadius = 0.7 );
SAL_DLLPRIVATE void ImplBlurContributions( const int aSize, const int aNumberOfContributions,
double* pBlurVector, double*& pWeights, int*& pPixels, int*& pCount );
public:
Bitmap();
......
......@@ -55,8 +55,20 @@ sal_Bool Bitmap::Filter( BmpFilter eFilter, const BmpFilterParam* pFilterParam,
{
case( BMP_FILTER_SMOOTH ):
{
const long pSmoothMatrix[] = { 1, 2, 1, 2, 5, 2, 1, 2, 1 };
bRet = ImplConvolute3( &pSmoothMatrix[ 0 ], 17, pFilterParam, pProgress );
// Blur for positive values of mnRadius
if (pFilterParam->mnRadius > 0.0)
{
bRet = ImplSeparableBlurFilter(pFilterParam->mnRadius);
}
// Unsharpen Mask for negative values of mnRadius
else if (pFilterParam->mnRadius < 0.0)
{
bRet = ImplSeparableUnsharpenFilter(pFilterParam->mnRadius);
}
else
{
bRet = sal_False;
}
}
break;
......@@ -1006,4 +1018,194 @@ sal_Bool Bitmap::ImplPopArt( const BmpFilterParam* /*pFilterParam*/, const Link*
return bRet;
}
double* MakeBlurKernel(const double radius, int& rows) {
int intRadius = (int) radius + 1.0;
rows = intRadius * 2 + 1;
double* matrix = new double[rows];
double sigma = radius / 3;
double radius2 = radius * radius;
int index = 0;
for (int row = -intRadius; row <= intRadius; row++)
{
double distance = row*row;
if (distance > radius2) {
matrix[index] = 0.0;
}else {
matrix[index] = exp( -distance / (2.0 * sigma * sigma) ) / sqrt( 2.0 * M_PI * sigma );
}
index++;
}
return matrix;
}
void Bitmap::ImplBlurContributions( const int aSize, const int aNumberOfContributions,
double* pBlurVector, double*& pWeights, int*& pPixels, int*& pCount )
{
pWeights = new double[ aSize*aNumberOfContributions ];
pPixels = new int[ aSize*aNumberOfContributions ];
pCount = new int[ aSize ];
int aLeft, aRight, aCurrentCount, aPixelIndex;
double aWeight;
for ( int i = 0; i < aSize; i++ )
{
aLeft = (int) i - aNumberOfContributions / 2;
aRight = (int) i + aNumberOfContributions / 2;
aCurrentCount = 0;
for ( int j = aLeft; j <= aRight; j++ )
{
aWeight = pBlurVector[aCurrentCount];
// Mirror edges
if (j < 0)
{
aPixelIndex = -j;
}
else if ( j >= aSize )
{
aPixelIndex = (aSize - j) + aSize - 1;
}
else
{
aPixelIndex = j;
}
// Edge case for small bitmaps
if ( aPixelIndex < 0 || aPixelIndex >= aSize )
{
aWeight = 0.0;
}
pWeights[ i*aNumberOfContributions + aCurrentCount ] = aWeight;
pPixels[ i*aNumberOfContributions + aCurrentCount ] = aPixelIndex;
aCurrentCount++;
}
pCount[ i ] = aCurrentCount;
}
}
// Separable Gaussian Blur
//
// Separable Gaussian Blur filter and accepts a blur radius
// as a parameter so the user can change the strength of the blur.
// Radius of 1.0 is 3 * standard deviation of gauss function.
//
// Separable Blur implementation uses 2x separable 1D convolution
// to process the image.
bool Bitmap::ImplSeparableBlurFilter(const double radius)
{
const long nWidth = GetSizePixel().Width();
const long nHeight = GetSizePixel().Height();
// Prepare Blur Vector
int aNumberOfContributions;
double* pBlurVector = MakeBlurKernel(radius, aNumberOfContributions);
double* pWeights;
int* pPixels;
int* pCount;
// Do horizontal filtering
ImplBlurContributions( nWidth, aNumberOfContributions, pBlurVector, pWeights, pPixels, pCount);
BitmapReadAccess* pReadAcc = AcquireReadAccess();
// switch coordinates as convolution pass transposes result
Bitmap aNewBitmap( Size( nHeight, nWidth ), 24 );
bool bResult = ImplConvolutionPass( aNewBitmap, nWidth, pReadAcc, aNumberOfContributions, pWeights, pPixels, pCount );
// Cleanup
ReleaseAccess( pReadAcc );
delete[] pWeights;
delete[] pPixels;
delete[] pCount;
if ( !bResult )
return bResult;
// Swap current bitmap with new bitmap
ImplAssignWithSize( aNewBitmap );
// Do vertical filtering
ImplBlurContributions(nHeight, aNumberOfContributions, pBlurVector, pWeights, pPixels, pCount );
pReadAcc = AcquireReadAccess();
aNewBitmap = Bitmap( Size( nWidth, nHeight ), 24 );
bResult = ImplConvolutionPass( aNewBitmap, nHeight, pReadAcc, aNumberOfContributions, pWeights, pPixels, pCount );
// Cleanup
ReleaseAccess( pReadAcc );
delete[] pWeights;
delete[] pCount;
delete[] pPixels;
delete[] pBlurVector;
if ( !bResult )
return bResult;
// Swap current bitmap with new bitmap
ImplAssignWithSize( aNewBitmap );
return true;
}
// Separable Unsharepn Mask filter is actually a substracted blured
// image from the original image.
bool Bitmap::ImplSeparableUnsharpenFilter(const double radius) {
const long nWidth = GetSizePixel().Width();
const long nHeight = GetSizePixel().Height();
Bitmap aBlur( *this );
aBlur.ImplSeparableBlurFilter(-radius);
// Amount of unsharpening effect on image - currently set to a fixed value
double aAmount = 2.0;
Bitmap aResultBitmap( Size( nWidth, nHeight ), 24);
BitmapReadAccess* pReadAccBlur = aBlur.AcquireReadAccess();
BitmapReadAccess* pReadAcc = AcquireReadAccess();
BitmapWriteAccess* pWriteAcc = aResultBitmap.AcquireWriteAccess();
BitmapColor aColor, aColorBlur;
// For all pixels in original image substract pixels values from blured image.
for( int x = 0; x < nWidth; x++ )
{
for( int y = 0; y < nHeight; y++ )
{
aColorBlur = pReadAccBlur->GetPixel( y , x );
if( pReadAccBlur->HasPalette() )
{
pReadAccBlur->GetPaletteColor( aColorBlur );
}
aColor = pReadAcc->GetPixel( y , x );
if( pReadAcc->HasPalette() )
{
aColor = pReadAcc->GetPaletteColor( aColor );
}
BitmapColor aResultColor(
(sal_uInt8) MinMax( aColor.GetRed() + (aColor.GetRed() - aColorBlur.GetRed()) * aAmount, 0, 255 ),
(sal_uInt8) MinMax( aColor.GetGreen() + (aColor.GetGreen() - aColorBlur.GetGreen()) * aAmount, 0, 255 ),
(sal_uInt8) MinMax( aColor.GetBlue() + (aColor.GetBlue() - aColorBlur.GetBlue()) * aAmount, 0, 255 ) );
pWriteAcc->SetPixel( y, x, aResultColor );
}
}
ReleaseAccess( pWriteAcc );
ReleaseAccess( pReadAcc );
ReleaseAccess( pReadAccBlur );
ImplAssignWithSize ( aResultBitmap );
return true;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment