import BinaryBitmap from '../../BinaryBitmap' ; import DecodeHintType from '../../DecodeHintType' ; import ResultPoint from '../../ResultPoint' ; import BitMatrix from '../../common/BitMatrix' ; import System from '../../util/System' ; import Arrays from '../../util/Arrays' ; import PDF417DetectorResult from './PDF417DetectorResult' ; import { float , int } from '../../../customTypings' ; author SITA Lab (kevin.osullivan sita author dswitkin google author Guenther Grau
  export default class Detector { private static INDEXES_START_PATTERN = Int32Array . from ( [ 0 , 4 , 1 , 5 ] ) ; private static INDEXES_STOP_PATTERN = Int32Array . from ( [ 6 , 2 , 7 , 3 ] ) ; private static MAX_AVG_VARIANCE : float = 0.42 ; private static MAX_INDIVIDUAL_VARIANCE : float = 0.8 ; private static START_PATTERN = Int32Array . from ( [ 8 , 1 , 1 , 1 , 1 , 1 , 1 , 3 ] ) ; private static STOP_PATTERN = Int32Array . from ( [ 7 , 1 , 1 , 3 , 1 , 1 , 1 , 2 , 1 ] ) ; private static MAX_PIXEL_DRIFT : number = 3 ; private static MAX_PATTERN_DRIFT : number = 5 ; private static SKIPPED_ROW_COUNT_MAX : number = 25 ; private static ROW_STEP : number = 5 ; private static BARCODE_MIN_HEIGHT : number = 10 ; param image param hints param multiple return { link throws public static detectMultiple ( image , hints , multiple ) { let bitMatrix = image . getBlackMatrix ( ) ; let barcodeCoordinates = Detector . detect ( multiple , bitMatrix ) ; if ( ! barcodeCoordinates . length ) { bitMatrix = bitMatrix . clone ( ) ; bitMatrix . rotate180 ( ) ; barcodeCoordinates = Detector . detect ( multiple , bitMatrix ) ; } return new PDF417DetectorResult ( bitMatrix , barcodeCoordinates ) ; } param multiple param bitMatrix return private static detect ( multiple , bitMatrix ) { const barcodeCoordinates = new Array < ResultPoint [ ] > ( ) ; let row = 0 ; let column = 0 ; let foundBarcodeInRow = false ; while ( row < bitMatrix . getHeight ( ) ) { const vertices = Detector . findVertices ( bitMatrix , row , column ) ; if ( vertices [ 0 ] == null && vertices [ 3 ] == null ) { if ( ! foundBarcodeInRow ) { break ; } foundBarcodeInRow = false ; column = 0 ; for ( const barcodeCoordinate of barcodeCoordinates ) { if ( barcodeCoordinate [ 1 ] != null ) { row = < int > Math . trunc ( Math . max ( row , barcodeCoordinate [ 1 ] . getY ( ) ) ) ; } if ( barcodeCoordinate [ 3 ] != null ) { row = Math . max ( row , < int > Math . trunc ( barcodeCoordinate [ 3 ] . getY ( ) ) ) ; } } row += Detector . ROW_STEP ; continue ; } foundBarcodeInRow = true ; barcodeCoordinates . push ( vertices ) ; if ( ! multiple ) { break ; } if ( vertices [ 2 ] != null ) { column = < int > Math . trunc ( vertices [ 2 ] . getX ( ) ) ; row = < int > Math . trunc ( vertices [ 2 ] . getY ( ) ) ; } else { column = < int > Math . trunc ( vertices [ 4 ] . getX ( ) ) ; row = < int > Math . trunc ( vertices [ 4 ] . getY ( ) ) ; } } return barcodeCoordinates ; } param matrix return private static findVertices ( matrix , startRow , startColumn ) { const height = matrix . getHeight ( ) ; const width = matrix . getWidth ( ) ; const result = new Array < ResultPoint > ( 8 ) ; Detector . copyToResult ( result , Detector . findRowsWithPattern ( matrix , height , width , startRow , startColumn , Detector . START_PATTERN ) , Detector . INDEXES_START_PATTERN ) ; if ( result [ 4 ] != null ) { startColumn = < int > Math . trunc ( result [ 4 ] . getX ( ) ) ; startRow = < int > Math . trunc ( result [ 4 ] . getY ( ) ) ; } Detector . copyToResult ( result , Detector . findRowsWithPattern ( matrix , height , width , startRow , startColumn , Detector . STOP_PATTERN ) , Detector . INDEXES_STOP_PATTERN ) ; return result ; } private static copyToResult ( result , tmpResult , destinationIndexes ) { for ( let i = 0 ; i < destinationIndexes . length ; i ++ ) { result [ destinationIndexes [ i ] ] = tmpResult [ i ] ; } } private static findRowsWithPattern ( matrix , height , width , startRow , startColumn , pattern ) { const result = new Array < ResultPoint > ( 4 ) ; let found = false ; const counters = new Int32Array ( pattern . length ) ; for ( ; startRow < height ; startRow += Detector . ROW_STEP ) { let loc = Detector . findGuardPattern ( matrix , startColumn , startRow , width , false , pattern , counters ) ; if ( loc != null ) { while ( startRow > 0 ) { const previousRowLoc = Detector . findGuardPattern ( matrix , startColumn , -- startRow , width , false , pattern , counters ) ; if ( previousRowLoc != null ) { loc = previousRowLoc ; } else { startRow ++ ; break ; } } result [ 0 ] = new ResultPoint ( loc [ 0 ] , startRow ) ; result [ 1 ] = new ResultPoint ( loc [ 1 ] , startRow ) ; found = true ; break ; } } let stopRow = startRow + 1 ; if ( found ) { let skippedRowCount = 0 ; let previousRowLoc = Int32Array . from ( [ < int > Math . trunc ( result [ 0 ] . getX ( ) ) , < int > Math . trunc ( result [ 1 ] . getX ( ) ) ] ) ; for ( ; stopRow < height ; stopRow ++ ) { const loc = Detector . findGuardPattern ( matrix , previousRowLoc [ 0 ] , stopRow , width , false , pattern , counters ) ; if ( loc != null && Math . abs ( previousRowLoc [ 0 ] - loc [ 0 ] ) < Detector . MAX_PATTERN_DRIFT && Math . abs ( previousRowLoc [ 1 ] - loc [ 1 ] ) < Detector . MAX_PATTERN_DRIFT ) { previousRowLoc = loc ; skippedRowCount = 0 ; } else { if ( skippedRowCount > Detector . SKIPPED_ROW_COUNT_MAX ) { break ; } else { skippedRowCount ++ ; } } } stopRow -= skippedRowCount + 1 ; result [ 2 ] = new ResultPoint ( previousRowLoc [ 0 ] , stopRow ) ; result [ 3 ] = new ResultPoint ( previousRowLoc [ 1 ] , stopRow ) ; } if ( stopRow - startRow < Detector . BARCODE_MIN_HEIGHT ) { Arrays . fill ( result , null ) ; } return result ; } param matrix param column param row param width param pattern param counters return private static findGuardPattern ( matrix , column , row , width , whiteFirst , pattern , counters ) { Arrays . fillWithin ( counters , 0 , counters . length , 0 ) ; let patternStart = column ; let pixelDrift = 0 ; while ( matrix . get ( patternStart , row ) && patternStart > 0 && pixelDrift ++ < Detector . MAX_PIXEL_DRIFT ) { patternStart -- ; } let x = patternStart ; let counterPosition = 0 ; let patternLength = pattern . length ; for ( let isWhite = whiteFirst ; x < width ; x ++ ) { let pixel = matrix . get ( x , row ) ; if ( pixel !== isWhite ) { counters [ counterPosition ] ++ ; } else { if ( counterPosition === patternLength - 1 ) { if ( Detector . patternMatchVariance ( counters , pattern , Detector . MAX_INDIVIDUAL_VARIANCE ) < Detector . MAX_AVG_VARIANCE ) { return new Int32Array ( [ patternStart , x ] ) ; } patternStart += counters [ 0 ] + counters [ 1 ] ; System . arraycopy ( counters , 2 , counters , 0 , counterPosition - 1 ) ; counters [ counterPosition - 1 ] = 0 ; counters [ counterPosition ] = 0 ; counterPosition -- ; } else { counterPosition ++ ; } counters [ counterPosition ] = 1 ; isWhite = ! isWhite ; } } if ( counterPosition === patternLength - 1 && Detector . patternMatchVariance ( counters , pattern , Detector . MAX_INDIVIDUAL_VARIANCE ) < Detector . MAX_AVG_VARIANCE ) { return new Int32Array ( [ patternStart , x - 1 ] ) ; } return null ; } param counters param pattern param maxIndividualVariance return private static patternMatchVariance ( counters , pattern , maxIndividualVariance ) { let numCounters = counters . length ; let total = 0 ; let patternLength = 0 ; for ( let i = 0 ; i < numCounters ; i ++ ) { total += counters [ i ] ; patternLength += pattern [ i ] ; } if ( total < patternLength ) { return Infinity ; } let unitBarWidth = < float > total / patternLength ; maxIndividualVariance *= unitBarWidth ; let totalVariance = 0.0 ; for ( let x = 0 ; x < numCounters ; x ++ ) { let counter = counters [ x ] ; let scaledPattern = pattern [ x ] * unitBarWidth ; let variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter ; if ( variance > maxIndividualVariance ) { return Infinity ; } totalVariance += variance ; } return totalVariance / total ; } }