/* * Copyright (C) 2008 The Android Open Source Project * * 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. */ /* ---- includes ----------------------------------------------------------- */ #include "b_BasicEM/Math.h" #include "b_BasicEM/Memory.h" #include "b_BasicEM/Int16Arr.h" #include "b_BasicEM/Int32Arr.h" #include "b_ImageEM/ToneDownBGSupp.h" /* ------------------------------------------------------------------------- */ /* ========================================================================= */ /* */ /* ---- \ghd{ auxiliary functions } ---------------------------------------- */ /* */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* ========================================================================= */ /* */ /* ---- \ghd{ constructor / destructor } ----------------------------------- */ /* */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* ========================================================================= */ /* */ /* ---- \ghd{ operators } -------------------------------------------------- */ /* */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* ========================================================================= */ /* */ /* ---- \ghd{ query functions } -------------------------------------------- */ /* */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* ========================================================================= */ /* */ /* ---- \ghd{ modify functions } ------------------------------------------- */ /* */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ /* ========================================================================= */ /* */ /* ---- \ghd{ I/O } -------------------------------------------------------- */ /* */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ /* ========================================================================= */ /* */ /* ---- \ghd{ exec functions } --------------------------------------------- */ /* */ /* ========================================================================= */ /* ------------------------------------------------------------------------- */ void bim_ToneDownBGSupp_BGGreyLevelOutside( struct bim_UInt8Image* imgA, struct bts_Int16Rect* rectA, int16 rectExpansionA, uint32* meanBGGrayLevelA ) { /* image access */ int16 iL, jL; uint8 *imgPtrL = 0; uint8 *imgPtrMaxL = 0; /* the sum is possibly a large number. e.g. for a 512x512 byte image, maximum brightness, sumL is 7x10E7 */ uint32 sumL, ctrL; /* the rectangle vertices */ int16 rectXMinL, rectXMaxL, rectYMinL, rectYMaxL; int16 rectIxXMinL, rectIxXMaxL, rectIxYMinL, rectIxYMaxL; /* expand the rectangle */ /* expand rectangle. the result is called the ROI */ rectXMinL = rectA->x1E + rectExpansionA; rectXMaxL = rectA->x2E - rectExpansionA; rectYMinL = rectA->y1E + rectExpansionA; rectYMaxL = rectA->y2E - rectExpansionA; rectIxXMinL = bbs_max( rectXMinL, ( int16 ) 0 ); rectIxXMaxL = bbs_max( rectXMaxL, ( int16 ) 0 ); rectIxXMaxL = bbs_min( rectXMaxL, ( int16 ) imgA->widthE ); rectIxYMinL = bbs_max( rectYMinL, ( int16 ) 0 ); rectIxYMaxL = bbs_min( rectYMaxL, ( int16 ) 0 ); rectIxYMaxL = bbs_min( rectYMaxL, ( int16 ) imgA->heightE ); /* avoid negative overlap */ rectIxXMinL = bbs_min( rectIxXMinL, rectIxXMaxL ); rectIxYMinL = bbs_min( rectIxYMinL, rectIxYMaxL ); /* printf( "new xmin=%d, xmax=%d, ymin=%d,ymax=%d \n", rectIxXMinL, rectIxXMaxL, rectIxYMinL, rectIxYMaxL ); */ /* part 1: sum up all the lines above the ROI */ sumL = 0; ctrL = 0; imgPtrL = &(imgA->arrE.arrPtrE[ 0 ]); ctrL += rectIxYMinL * imgA->widthE; imgPtrMaxL = imgPtrL + rectIxYMinL * imgA->widthE; while ( imgPtrL < imgPtrMaxL ) { sumL += *imgPtrL; imgPtrL++; } /* part 2: sum up all the lines below the ROI */ ctrL += ( imgA->heightE - rectIxYMaxL ) * imgA->widthE; imgPtrL = &(imgA->arrE.arrPtrE[ rectIxYMaxL * imgA->widthE ]); imgPtrMaxL = &(imgA->arrE.arrPtrE[ imgA->heightE * imgA->widthE ]); while ( imgPtrL < imgPtrMaxL ) { sumL += *imgPtrL; imgPtrL++; } /* part 3: sum over the two vertically adjacent blocks */ for ( jL = rectIxYMinL; jL < rectIxYMaxL; jL++ ) { imgPtrL = &(imgA->arrE.arrPtrE[ rectIxYMinL * imgA->widthE ]); ctrL += bbs_max( 0, rectIxXMinL ); for ( iL = 0; iL < rectIxXMinL; iL++ ) { sumL += imgPtrL[ iL ]; } if( ( int32 )imgA->widthE > ( int32 )rectIxXMaxL ) { ctrL += ( int32 )imgA->widthE - ( int32 )rectIxXMaxL; } for ( iL = rectIxXMaxL; iL < ( int16 ) imgA->widthE; iL++ ) { sumL += imgPtrL[ iL ]; } } /* printf( "new sum = %d, new ctr = %d \n", sumL, ctrL ); */ /* result is bpb=[16.16] */ *meanBGGrayLevelA = ( sumL << 16 ) / ( uint32 ) ctrL; /* result is bpb=[16.16] */ *meanBGGrayLevelA = sumL / ctrL; /* integer division */ sumL = sumL - *meanBGGrayLevelA * ctrL; /* result always greater than or equal to zero */ *meanBGGrayLevelA = *meanBGGrayLevelA << 16; /* shift to left */ *meanBGGrayLevelA = *meanBGGrayLevelA + ( sumL << 16 ) / ctrL; /* add residue */ } /* ------------------------------------------------------------------------- */ void bim_ToneDownBGSupp_BGGreyLevelContour( struct bim_UInt8Image* imgA, struct bts_Int16Rect* rectA, uint32* meanBGGrayLevelA ) { /* image access */ int16 iL; uint8 *imgPtr0L = 0; uint8 *imgPtr1L = 0; /* the sum is possibly a large number. e.g. for a 512x512 byte image, maximum brightness, sumL is 7x10E7 */ uint32 sumL, ctrL; /* the rectangle vertices */ int16 rectXMinL, rectXMaxL, rectYMinL, rectYMaxL; int16 rectIxXMinL, rectIxXMaxL, rectIxYMinL, rectIxYMaxL; int16 rectMinWidthL = 10, rectMinHeightL = 10; int16 rectXMidPointL, rectYMidPointL; int16 shiftXRectL, shiftYRectL; /* cut off the rectangle at the image bounaries * when its size becomes too small * the rectangle is shifted back inside the image */ /* cut off at image boundaries */ rectXMinL = rectA->x1E; rectXMaxL = rectA->x2E; rectYMinL = rectA->y1E; rectYMaxL = rectA->y2E; rectIxXMinL = bbs_max( rectXMinL, ( int16 ) 0 ); rectIxXMaxL = bbs_max( rectXMaxL, ( int16 ) 0 ); rectIxXMaxL = bbs_min( rectXMaxL, ( int16 ) imgA->widthE ); rectIxYMinL = bbs_max( rectYMinL, ( int16 ) 0 ); rectIxYMaxL = bbs_min( rectYMaxL, ( int16 ) 0 ); rectIxYMaxL = bbs_min( rectYMaxL, ( int16 ) imgA->heightE ); /* shift back into image */ shiftXRectL = 0; shiftYRectL = 0; if ( rectIxXMaxL - rectIxXMinL < rectMinWidthL ) { rectXMidPointL = ( rectIxXMaxL + rectIxXMinL ) >> 1; rectIxXMinL = rectXMidPointL - ( rectMinWidthL >> 1 ); rectIxXMaxL = rectXMidPointL + ( rectMinWidthL >> 1 ); if ( rectIxXMinL < 0 ) { shiftXRectL = -rectIxXMinL; } if ( rectIxXMaxL > ( int16 ) imgA->widthE ) { shiftXRectL = rectIxXMaxL - ( int16 ) imgA->widthE; } } if ( rectIxYMaxL - rectIxYMinL < rectMinHeightL ) { rectYMidPointL = ( rectIxYMaxL + rectIxYMinL ) >> 1; rectIxYMinL = rectYMidPointL - ( rectMinWidthL >> 1 ); rectIxYMaxL = rectYMidPointL + ( rectMinWidthL >> 1 ); if ( rectIxYMinL < 0 ) { shiftXRectL = -rectIxYMinL; } if ( rectIxYMaxL > ( int16 ) imgA->widthE ) { shiftXRectL = rectIxYMaxL - ( int16 ) imgA->widthE; } } rectIxXMinL += shiftXRectL; rectIxXMaxL += shiftXRectL; rectIxYMinL += shiftYRectL; rectIxYMaxL += shiftYRectL; /* when the image is small, there is a possibility that the shifted rectangle lies outside of the image. * => lop off the rectangle at image boundaries once again */ rectIxXMinL = bbs_max( rectXMinL, ( int16 ) 0 ); rectIxXMaxL = bbs_min( rectXMaxL, ( int16 ) imgA->widthE ); rectIxYMinL = bbs_max( rectYMinL, ( int16 ) 0 ); rectIxYMaxL = bbs_min( rectYMaxL, ( int16 ) imgA->heightE ); sumL = 0; ctrL = 0; ctrL += ( rectIxXMaxL - rectIxXMinL ) << 1; ctrL += ( rectIxYMaxL - rectIxYMinL - 2 ) << 1; /* loop over the contour */ imgPtr0L = &(imgA->arrE.arrPtrE[ rectIxYMinL * imgA->widthE ]); imgPtr1L = &(imgA->arrE.arrPtrE[ ( rectIxYMaxL - 1 ) * imgA->widthE ]); for ( iL = rectIxXMinL; iL < rectIxXMaxL; iL++ ) { sumL += imgPtr0L[ iL ]; sumL += imgPtr1L[ iL ]; } imgPtr0L = &(imgA->arrE.arrPtrE[ ( rectIxYMinL + 1 ) * imgA->widthE + rectIxXMinL ]); imgPtr1L = &(imgA->arrE.arrPtrE[ ( rectIxYMinL + 1 ) * imgA->widthE + rectIxXMaxL - 1 ]); for ( iL = rectIxYMinL + 1; iL < rectIxYMaxL - 1; iL++ ) { sumL += *imgPtr0L; sumL += *imgPtr1L; imgPtr0L += imgA->widthE; imgPtr1L += imgA->widthE; } /* printf( "new sum = %d, new ctr = %d \n", sumL, ctrL ); */ /* result is bpb=[16.16] */ *meanBGGrayLevelA = ( sumL << 16 ) / ( uint32 ) ctrL; /* result is bpb=[16.16] */ *meanBGGrayLevelA = sumL / ctrL; /* integer division */ sumL = sumL - *meanBGGrayLevelA * ctrL; /* result always greater than or equal to zero */ *meanBGGrayLevelA = *meanBGGrayLevelA << 16; /* shift to left */ *meanBGGrayLevelA = *meanBGGrayLevelA + ( sumL << 16 ) / ctrL; /* add residue */ } /* ------------------------------------------------------------------------- */ void bim_ToneDownBGSupp_suppress( struct bim_UInt8Image* imgA, struct bts_Int16Rect* rectA, int16 rectShrinkageA, int32 toneDownFactorA, /* ToDo: change to int16, bpb=[0.16] */ int32 cutOffAccuracyA ) { /* ((( variable declarations begin ))) */ /* the rectangle vertices */ int16 rectXMinL, rectXMaxL, rectYMinL, rectYMaxL; int16 rectIxXMinL, rectIxXMaxL, rectIxYMinL, rectIxYMaxL; int16 rectShrinkageL; /* the BG mean grey value */ uint8 meanBGGreyBBPL; uint32 meanBGGreyLevelL; uint32 meanBGGreyLevelByteL; int32 meanBGGreyLevelLongL; /* maximum reach of the ROI */ uint32 maxROIReachL; int16 rOIReachXMinL, rOIReachXMaxL, rOIReachYMinL, rOIReachYMaxL; int16 rOIReachIxXMinL, rOIReachIxXMaxL, rOIReachIxYMinL, rOIReachIxYMaxL; int16 ridgeIxLeftL, ridgeIxRightL; /* tone down table */ struct bbs_Int32Arr toneDownFactorsL; /* ToDo: change int32 bpb=[16.16] to uint bpb=[0.16] */ int32 toneDownFactorPowA; int32* toneDownFactorsPtrL; int32 ctrL; /* image access */ int16 iL, jL; uint8 *imgPtrL = 0; /* welcome back to the stoneage */ /* weighting formula */ int32 weightL, invWeightL; /* R=[0.0...1.0], bpb=[16.16] */ int32 opSrcL, opBGL, sumL; /* R=[0.0...255.0], bpb=[24,8] */ /* ((( variable declarations end ))) */ /* make sure that the width is smaller than the rectangle */ rectShrinkageL = rectShrinkageA; rectShrinkageL = bbs_min( rectShrinkageL, ( rectA->x2E - rectA->x1E ) >> 1 ); rectShrinkageL = bbs_min( rectShrinkageL, ( rectA->y2E - rectA->y1E ) >> 1 ); /* shrink rectangle. the result is called the ROI */ rectXMinL = rectA->x1E + rectShrinkageL; rectXMaxL = rectA->x2E - rectShrinkageL; rectYMinL = rectA->y1E + rectShrinkageL; rectYMaxL = rectA->y2E - rectShrinkageL; rectIxXMinL = bbs_max( rectXMinL, 0 ); rectIxXMinL = bbs_min( rectIxXMinL, ( int16 ) imgA->widthE ); rectIxXMaxL = bbs_min( rectXMaxL, ( int16 ) imgA->widthE ); rectIxXMaxL = bbs_max( rectIxXMaxL, 0 ); rectIxYMinL = bbs_max( rectYMinL, 0 ); rectIxYMinL = bbs_min( rectIxYMinL, ( int16 ) imgA->heightE ); rectIxYMaxL = bbs_min( rectYMaxL, ( int16 ) imgA->heightE ); rectIxYMaxL = bbs_max( rectIxYMaxL, 0 ); /* exit function at exceptional cases */ if ( ( imgA->heightE == 0 ) || ( imgA->widthE == 0 ) ) return; if ( rectShrinkageL == 0 ) return; /* compute the mean gray level aloong the rectangle contour */ bim_ToneDownBGSupp_BGGreyLevelContour( imgA, rectA, &meanBGGreyLevelL ); /* printf( "new mean BG gray value = %f \n", ( float ) meanBGGreyLevelL / 65536.0f ); */ /* R=[0.0...255.0], bpb=[24.8] */ meanBGGreyBBPL = 16; meanBGGreyLevelL = ( 128 << meanBGGreyBBPL ); meanBGGreyLevelByteL = meanBGGreyLevelL >> meanBGGreyBBPL; meanBGGreyLevelLongL = ( 128 << meanBGGreyBBPL ); /* ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo ToDo */ /* this function computes an image that moving away from the ROI gradually fades to * the background grey level BG according to the formula * tonedImg = w srcImg + (1-w) BG * w depends on the distance to the ROI. * there is a distance maxROIReachL beyond which * the importance of the source image * relative to the BG in the equation * falls below a small threshold. * in those regions the toned image is equal to * the mean BG grey value. i.e. w=0, tonedImg = BG */ maxROIReachL = bbs_max( imgA->widthE, imgA->heightE ); /* pre-compute an array of tone down factors. R=[0.0...1.0] => bpb=[0.16] (idealy, bpb=[16.16] due to missing uInt16Arr ) */ bbs_Int32Arr_init( &toneDownFactorsL ); bbs_Int32Arr_size( &toneDownFactorsL, maxROIReachL ); toneDownFactorPowA = toneDownFactorA; toneDownFactorsPtrL = toneDownFactorsL.arrPtrE; for( ctrL = 0; ctrL < ( int32 ) maxROIReachL && toneDownFactorPowA > cutOffAccuracyA; ctrL++ ) { toneDownFactorsPtrL[ ctrL ] = toneDownFactorPowA; toneDownFactorPowA = toneDownFactorPowA * ( toneDownFactorA >> 1 ); toneDownFactorPowA = toneDownFactorPowA >> 15; /* make active to check the error that accumulates by recursively multiplying factors */ /* printf( "pow = %d, tonedown dec = %d, tonedown float = %f \n", ctrL + 2, toneDownFactorPowA, toneDownFactorPowA / 65536.0f ); */ } maxROIReachL = ctrL; /* printf( "maxROIReachL = %d, tonedown = %d \n", maxROIReachL, toneDownFactorPowA ); */ /* move across the image one row at a time. * (1) fill the outside frame with BG grey level * (2) blend in the original image moving towards the ROI */ rOIReachXMinL = rectXMinL - ( int32 ) maxROIReachL; rOIReachXMaxL = rectXMaxL + ( int32 ) maxROIReachL; rOIReachYMinL = rectYMinL - ( int32 ) maxROIReachL; rOIReachYMaxL = rectYMaxL + ( int32 ) maxROIReachL; rOIReachIxXMinL = bbs_max( rOIReachXMinL, ( int16 ) 0 ); rOIReachIxXMinL = bbs_min( rOIReachIxXMinL, ( int16 ) imgA->widthE ); rOIReachIxXMaxL = bbs_min( rOIReachXMaxL, ( int16 ) imgA->widthE ); rOIReachIxXMaxL = bbs_max( rOIReachIxXMaxL, ( int16 ) 0 ); rOIReachIxYMinL = bbs_max( rOIReachYMinL, ( int16 ) 0 ); rOIReachIxYMinL = bbs_min( rOIReachIxYMinL, ( int16 ) imgA->heightE ); rOIReachIxYMaxL = bbs_min( rOIReachYMaxL, ( int16 ) imgA->heightE ); rOIReachIxYMaxL = bbs_max( rOIReachIxYMaxL, ( int16 ) 0 ); /* (1) far from the ROI the image is filled with the BG grey value */ imgPtrL = 0; for ( jL = 0; jL < rOIReachYMinL; jL++ ) { imgPtrL = &( imgA->arrE.arrPtrE[ jL * imgA->widthE ] ); for ( iL = 0; iL <= ( int16 ) imgA->widthE; iL++ ) { imgPtrL[ iL ] = meanBGGreyLevelByteL; } } for ( jL = rOIReachYMaxL; jL < ( int16 ) imgA->heightE; jL++ ) { imgPtrL = &( imgA->arrE.arrPtrE[ jL * imgA->widthE ] ); for ( iL = 0; iL <= ( int16 ) imgA->widthE; iL++ ) { imgPtrL[ iL ] = meanBGGreyLevelByteL; } } for ( jL = rOIReachIxYMinL; jL < rOIReachIxYMaxL; jL++ ) { imgPtrL = &( imgA->arrE.arrPtrE[ jL * imgA->widthE ] ); for ( iL = 0; iL < rOIReachXMinL; iL++ ) { imgPtrL[ iL ] = meanBGGreyLevelByteL; } for ( iL = rOIReachXMaxL; iL < ( int16 ) imgA->widthE; iL++ ) { imgPtrL[ iL ] = meanBGGreyLevelByteL; } } /* (2) blend from ROI to outside regions */ for ( jL = rOIReachIxYMinL; jL < rectIxYMinL; jL++ ) { /* the factor for one row is a constant */ weightL = ( int32 ) toneDownFactorsPtrL[ maxROIReachL - 1 - ( jL - rOIReachYMinL ) ]; invWeightL = 0x00010000 - weightL; opBGL = ( meanBGGreyLevelLongL >> 9 ) * invWeightL; /* result is bpb=[8,24] */ opBGL = opBGL >> 7; imgPtrL = &( imgA->arrE.arrPtrE[ jL * imgA->widthE ] ); /* compute the ridge position */ ridgeIxLeftL = bbs_max( 0, rOIReachXMinL + jL - rOIReachYMinL ); ridgeIxRightL = bbs_min( ( int16 ) imgA->widthE - 1, rOIReachXMaxL - 1 - ( jL - rOIReachYMinL ) ); /* loop over all elements from left ridge through right ridge */ for ( iL = ridgeIxLeftL; iL <= ridgeIxRightL; iL++ ) { opSrcL = imgPtrL[ iL ]; /* leave at byte */ opSrcL = opSrcL * weightL; /* result is bpb=[16,16] */ sumL = opSrcL + opBGL; /* OF impossible */ imgPtrL[ iL ] = sumL >> 16; /* round to byte */ } } for ( jL = rOIReachIxYMaxL - 1; jL >= rectIxYMaxL; jL-- ) { /* the factor for one row is a constant */ weightL = ( int32 ) toneDownFactorsPtrL[ maxROIReachL - 1 - ( rOIReachYMaxL - 1 - jL ) ]; invWeightL = 0x00010000 - weightL; opBGL = ( meanBGGreyLevelLongL >> 9 ) * invWeightL; /* result is bpb=[8,24] */ opBGL = opBGL >> 7; imgPtrL = &( imgA->arrE.arrPtrE[ jL * imgA->widthE ] ); /* compute the ridge position */ ridgeIxLeftL = bbs_max( 0, rOIReachXMinL + ( rOIReachYMaxL - 1 - jL ) ); ridgeIxRightL = bbs_min( ( int16 ) imgA->widthE - 1, rOIReachXMaxL - 1 - ( rOIReachYMaxL - 1 - jL ) ); /* loop over all elements from left ridge through right ridge */ for ( iL = ridgeIxLeftL; iL <= ridgeIxRightL; iL++ ) { opSrcL = imgPtrL[ iL ]; /* leave at byte */ opSrcL = opSrcL * weightL; /* result is bpb=[16,16] */ sumL = opSrcL + opBGL; /* OF impossible */ imgPtrL[ iL ] = sumL >> 16; /* round to byte */ } } for ( jL = rOIReachIxYMinL; jL < rOIReachIxYMaxL; jL++ ) { imgPtrL = &( imgA->arrE.arrPtrE[ jL * imgA->widthE ] ); ridgeIxLeftL = bbs_min( rOIReachXMinL + ( jL - rOIReachYMinL ) - 1, rectXMinL - 1 ); ridgeIxLeftL = bbs_min( ridgeIxLeftL, rOIReachXMinL + ( rOIReachYMaxL - 1 - jL ) - 1 ); for ( iL = rOIReachIxXMinL; iL <= ridgeIxLeftL; iL++ ) { weightL = ( int32 ) toneDownFactorsPtrL[ maxROIReachL - 1 - ( iL - rOIReachXMinL ) ]; invWeightL = 0x00010000 - weightL; opBGL = ( meanBGGreyLevelLongL >> 9 ) * invWeightL; /* result is bpb=[16,16] */ opBGL = opBGL >> 7; opSrcL = imgPtrL[ iL ]; /* leave at byte */ opSrcL = opSrcL * weightL; /* result is bpb=[16,16] */ sumL = opSrcL + opBGL; /* OF impossible */ imgPtrL[ iL ] = sumL >> 16; /* round to byte */ } ridgeIxRightL = bbs_max( rOIReachXMaxL - 1 - ( jL - rOIReachYMinL ) + 1 , rectXMaxL ); ridgeIxRightL = bbs_max( ridgeIxRightL, rOIReachXMaxL - 1 - ( rOIReachYMaxL - 1 - jL ) + 1 ); for ( iL = ridgeIxRightL; iL < rOIReachIxXMaxL; iL++ ) { weightL = ( int32 ) toneDownFactorsPtrL[ iL - rectXMaxL ]; invWeightL = 0x00010000 - weightL; opBGL = ( meanBGGreyLevelLongL >> 9 ) * invWeightL; /* result is bpb=[16,16] */ opBGL = opBGL >> 7; opSrcL = imgPtrL[ iL ]; /* leave at byte */ opSrcL = opSrcL * weightL; /* result is bpb=[16,16] */ sumL = opSrcL + opBGL; /* OF impossible */ imgPtrL[ iL ] = sumL >> 16; /* round to byte */ } } } /* ------------------------------------------------------------------------- */ /* ========================================================================= */