import ResultPoint from '../../ResultPoint' ; import AztecDetectorResult from '../AztecDetectorResult' ; import BitMatrix from '../../common/BitMatrix' ; import CornerDetector from '../../common/detector/CornerDetector' ; import MathUtils from '../../common/detector/MathUtils' ; import WhiteRectangleDetector from '../../common/detector/WhiteRectangleDetector' ; import GenericGF from '../../common/reedsolomon/GenericGF' ; import ReedSolomonDecoder from '../../common/reedsolomon/ReedSolomonDecoder' ; import NotFoundException from '../../NotFoundException' ; import GridSamplerInstance from '../../common/GridSamplerInstance' ; import Integer from '../../util/Integer' ; export class Point { private x : number ; private y : number ; public toResultPoint ( ) { return new ResultPoint ( this . getX ( ) , this . getY ( ) ) ; } public constructor ( x , y ) { this . x = x ; this . y = y ; } public getX ( ) { return this . x ; } public getY ( ) { return this . y ; } } author David Olivier
 *  author Frank Yellin
  export default class Detector { private EXPECTED_CORNER_BITS = new Int32Array ( [ 0xee0 , 0x1dc , 0x83b , 0x707 , ] ) ; private image : BitMatrix ; private compact : boolean ; private nbLayers : number ; private nbDataBlocks : number ; private nbCenterLayers : number ; private shift : number ; public constructor ( image ) { this . image = image ; } public detect ( ) { return this . detectMirror ( false ) ; } param isMirror return { link throws public detectMirror ( isMirror ) { let pCenter = this . getMatrixCenter ( ) ; let bullsEyeCorners = this . getBullsEyeCorners ( pCenter ) ; if ( isMirror ) { let temp = bullsEyeCorners [ 0 ] ; bullsEyeCorners [ 0 ] = bullsEyeCorners [ 2 ] ; bullsEyeCorners [ 2 ] = temp ; } this . extractParameters ( bullsEyeCorners ) ; let bits = this . sampleGrid ( this . image , bullsEyeCorners [ this . shift % 4 ] , bullsEyeCorners [ ( this . shift + 1 ) % 4 ] , bullsEyeCorners [ ( this . shift + 2 ) % 4 ] , bullsEyeCorners [ ( this . shift + 3 ) % 4 ] ) ; let corners = this . getMatrixCornerPoints ( bullsEyeCorners ) ; return new AztecDetectorResult ( bits , corners , this . compact , this . nbDataBlocks , this . nbLayers ) ; } param bullsEyeCorners throws private extractParameters ( bullsEyeCorners ) { if ( ! this . isValidPoint ( bullsEyeCorners [ 0 ] ) || ! this . isValidPoint ( bullsEyeCorners [ 1 ] ) || ! this . isValidPoint ( bullsEyeCorners [ 2 ] ) || ! this . isValidPoint ( bullsEyeCorners [ 3 ] ) ) { throw new NotFoundException ( ) ; } let length = 2 * this . nbCenterLayers ; let sides = new Int32Array ( [ this . sampleLine ( bullsEyeCorners [ 0 ] , bullsEyeCorners [ 1 ] , length ) , this . sampleLine ( bullsEyeCorners [ 1 ] , bullsEyeCorners [ 2 ] , length ) , this . sampleLine ( bullsEyeCorners [ 2 ] , bullsEyeCorners [ 3 ] , length ) , this . sampleLine ( bullsEyeCorners [ 3 ] , bullsEyeCorners [ 0 ] , length ) ] ) ; this . shift = this . getRotation ( sides , length ) ; let parameterData = 0 ; for ( let i = 0 ; i < 4 ; i ++ ) { let side = sides [ ( this . shift + i ) % 4 ] ; if ( this . compact ) { parameterData <<= 7 ; parameterData += ( side >> 1 ) & 0x7F ; } else { parameterData <<= 10 ; parameterData += ( ( side >> 2 ) & ( 0x1f << 5 ) ) + ( ( side >> 1 ) & 0x1F ) ; } } let correctedData = this . getCorrectedParameterData ( parameterData , this . compact ) ; if ( this . compact ) { this . nbLayers = ( correctedData >> 6 ) + 1 ; this . nbDataBlocks = ( correctedData & 0x3F ) + 1 ; } else { this . nbLayers = ( correctedData >> 11 ) + 1 ; this . nbDataBlocks = ( correctedData & 0x7FF ) + 1 ; } } private getRotation ( sides , length ) { let cornerBits = 0 ; sides . forEach ( ( side , idx , arr ) => { let t = ( ( side >> ( length - 2 ) ) << 1 ) + ( side & 1 ) ; cornerBits = ( cornerBits << 3 ) + t ; } ) ; cornerBits = ( ( cornerBits & 1 ) << 11 ) + ( cornerBits >> 1 ) ; for ( let shift = 0 ; shift < 4 ; shift ++ ) { if ( Integer . bitCount ( cornerBits ^ this . EXPECTED_CORNER_BITS [ shift ] ) <= 2 ) { return shift ; } } throw new NotFoundException ( ) ; } param parameterData param compact throws private getCorrectedParameterData ( parameterData , compact ) { let numCodewords ; let numDataCodewords ; if ( compact ) { numCodewords = 7 ; numDataCodewords = 2 ; } else { numCodewords = 10 ; numDataCodewords = 4 ; } let numECCodewords = numCodewords - numDataCodewords ; let parameterWords = new Int32Array ( numCodewords ) ; for ( let i = numCodewords - 1 ; i >= 0 ; -- i ) { parameterWords [ i ] = parameterData & 0xF ; parameterData >>= 4 ; } try { let rsDecoder = new ReedSolomonDecoder ( GenericGF . AZTEC_PARAM ) ; rsDecoder . decode ( parameterWords , numECCodewords ) ; } catch ( ignored ) { throw new NotFoundException ( ) ; } let result = 0 ; for ( let i = 0 ; i < numDataCodewords ; i ++ ) { result = ( result << 4 ) + parameterWords [ i ] ; } return result ; } param pCenter return throws private getBullsEyeCorners ( pCenter ) { let pina = pCenter ; let pinb = pCenter ; let pinc = pCenter ; let pind = pCenter ; let color = true ; for ( this . nbCenterLayers = 1 ; this . nbCenterLayers < 9 ; this . nbCenterLayers ++ ) { let pouta = this . getFirstDifferent ( pina , color , 1 , - 1 ) ; let poutb = this . getFirstDifferent ( pinb , color , 1 , 1 ) ; let poutc = this . getFirstDifferent ( pinc , color , - 1 , 1 ) ; let poutd = this . getFirstDifferent ( pind , color , - 1 , - 1 ) ; if ( this . nbCenterLayers > 2 ) { let q = ( this . distancePoint ( poutd , pouta ) * this . nbCenterLayers ) / ( this . distancePoint ( pind , pina ) * ( this . nbCenterLayers + 2 ) ) ; if ( q < 0.75 || q > 1.25 || ! this . isWhiteOrBlackRectangle ( pouta , poutb , poutc , poutd ) ) { break ; } } pina = pouta ; pinb = poutb ; pinc = poutc ; pind = poutd ; color = ! color ; } if ( this . nbCenterLayers !== 5 && this . nbCenterLayers !== 7 ) { throw new NotFoundException ( ) ; } this . compact = this . nbCenterLayers === 5 ; let pinax = new ResultPoint ( pina . getX ( ) + 0.5 , pina . getY ( ) - 0.5 ) ; let pinbx = new ResultPoint ( pinb . getX ( ) + 0.5 , pinb . getY ( ) + 0.5 ) ; let pincx = new ResultPoint ( pinc . getX ( ) - 0.5 , pinc . getY ( ) + 0.5 ) ; let pindx = new ResultPoint ( pind . getX ( ) - 0.5 , pind . getY ( ) - 0.5 ) ; return this . expandSquare ( [ pinax , pinbx , pincx , pindx ] , 2 * this . nbCenterLayers - 3 , 2 * this . nbCenterLayers ) ; } return private getMatrixCenter ( ) { let pointA ; let pointB ; let pointC ; let pointD ; try { let cornerPoints = new WhiteRectangleDetector ( this . image ) . detect ( ) ; pointA = cornerPoints [ 0 ] ; pointB = cornerPoints [ 1 ] ; pointC = cornerPoints [ 2 ] ; pointD = cornerPoints [ 3 ] ; } catch ( e ) { let cx = this . image . getWidth ( ) / 2 ; let cy = this . image . getHeight ( ) / 2 ; pointA = this . getFirstDifferent ( new Point ( cx + 7 , cy - 7 ) , false , 1 , - 1 ) . toResultPoint ( ) ; pointB = this . getFirstDifferent ( new Point ( cx + 7 , cy + 7 ) , false , 1 , 1 ) . toResultPoint ( ) ; pointC = this . getFirstDifferent ( new Point ( cx - 7 , cy + 7 ) , false , - 1 , 1 ) . toResultPoint ( ) ; pointD = this . getFirstDifferent ( new Point ( cx - 7 , cy - 7 ) , false , - 1 , - 1 ) . toResultPoint ( ) ; } let cx = MathUtils . round ( ( pointA . getX ( ) + pointD . getX ( ) + pointB . getX ( ) + pointC . getX ( ) ) / 4.0 ) ; let cy = MathUtils . round ( ( pointA . getY ( ) + pointD . getY ( ) + pointB . getY ( ) + pointC . getY ( ) ) / 4.0 ) ; try { let cornerPoints = new WhiteRectangleDetector ( this . image , 15 , cx , cy ) . detect ( ) ; pointA = cornerPoints [ 0 ] ; pointB = cornerPoints [ 1 ] ; pointC = cornerPoints [ 2 ] ; pointD = cornerPoints [ 3 ] ; } catch ( e ) { pointA = this . getFirstDifferent ( new Point ( cx + 7 , cy - 7 ) , false , 1 , - 1 ) . toResultPoint ( ) ; pointB = this . getFirstDifferent ( new Point ( cx + 7 , cy + 7 ) , false , 1 , 1 ) . toResultPoint ( ) ; pointC = this . getFirstDifferent ( new Point ( cx - 7 , cy + 7 ) , false , - 1 , 1 ) . toResultPoint ( ) ; pointD = this . getFirstDifferent ( new Point ( cx - 7 , cy - 7 ) , false , - 1 , - 1 ) . toResultPoint ( ) ; } cx = MathUtils . round ( ( pointA . getX ( ) + pointD . getX ( ) + pointB . getX ( ) + pointC . getX ( ) ) / 4.0 ) ; cy = MathUtils . round ( ( pointA . getY ( ) + pointD . getY ( ) + pointB . getY ( ) + pointC . getY ( ) ) / 4.0 ) ; return new Point ( cx , cy ) ; } param bullsEyeCorners return private getMatrixCornerPoints ( bullsEyeCorners ) { return this . expandSquare ( bullsEyeCorners , 2 * this . nbCenterLayers , this . getDimension ( ) ) ; } /**
     * Creates a BitMatrix by sampling the provided image.
     * topLeft, topRight, bottomRight, and bottomLeft are the centers of the squares on the
     * diagonal just outside the bull's eye.
     */ private sampleGrid ( image , topLeft , topRight , bottomRight , bottomLeft ) { let sampler = GridSamplerInstance . getInstance ( ) ; let dimension = this . getDimension ( ) ; let low = dimension / 2 - this . nbCenterLayers ; let high = dimension / 2 + this . nbCenterLayers ; return sampler . sampleGrid ( image , dimension , dimension , low , low , high , low , high , high , low , high , topLeft . getX ( ) , topLeft . getY ( ) , topRight . getX ( ) , topRight . getY ( ) , bottomRight . getX ( ) , bottomRight . getY ( ) , bottomLeft . getX ( ) , bottomLeft . getY ( ) ) ; } param p1 param p2 param size return private sampleLine ( p1 , p2 , size ) { let result = 0 ; let d = this . distanceResultPoint ( p1 , p2 ) ; let moduleSize = d / size ; let px = p1 . getX ( ) ; let py = p1 . getY ( ) ; let dx = moduleSize * ( p2 . getX ( ) - p1 . getX ( ) ) / d ; let dy = moduleSize * ( p2 . getY ( ) - p1 . getY ( ) ) / d ; for ( let i = 0 ; i < size ; i ++ ) { if ( this . image . get ( MathUtils . round ( px + i * dx ) , MathUtils . round ( py + i * dy ) ) ) { result |= 1 << ( size - i - 1 ) ; } } return result ; } return private isWhiteOrBlackRectangle ( p1 , p2 , p3 , p4 ) { let corr = 3 ; p1 = new Point ( p1 . getX ( ) - corr , p1 . getY ( ) + corr ) ; p2 = new Point ( p2 . getX ( ) - corr , p2 . getY ( ) - corr ) ; p3 = new Point ( p3 . getX ( ) + corr , p3 . getY ( ) - corr ) ; p4 = new Point ( p4 . getX ( ) + corr , p4 . getY ( ) + corr ) ; let cInit = this . getColor ( p4 , p1 ) ; if ( cInit === 0 ) { return false ; } let c = this . getColor ( p1 , p2 ) ; if ( c !== cInit ) { return false ; } c = this . getColor ( p2 , p3 ) ; if ( c !== cInit ) { return false ; } c = this . getColor ( p3 , p4 ) ; return c === cInit ; } return private getColor ( p1 , p2 ) { let d = this . distancePoint ( p1 , p2 ) ; let dx = ( p2 . getX ( ) - p1 . getX ( ) ) / d ; let dy = ( p2 . getY ( ) - p1 . getY ( ) ) / d ; let error = 0 ; let px = p1 . getX ( ) ; let py = p1 . getY ( ) ; let colorModel = this . image . get ( p1 . getX ( ) , p1 . getY ( ) ) ; let iMax = Math . ceil ( d ) ; for ( let i = 0 ; i < iMax ; i ++ ) { px += dx ; py += dy ; if ( this . image . get ( MathUtils . round ( px ) , MathUtils . round ( py ) ) !== colorModel ) { error ++ ; } } let errRatio = error / d ; if ( errRatio > 0.1 && errRatio < 0.9 ) { return 0 ; } return ( errRatio <= 0.1 ) === colorModel ? 1 : - 1 ; } /**
     * Gets the coordinate of the first point with a different color in the given direction
     */ private getFirstDifferent ( init , color , dx , dy ) { let x = init . getX ( ) + dx ; let y = init . getY ( ) + dy ; while ( this . isValid ( x , y ) && this . image . get ( x , y ) === color ) { x += dx ; y += dy ; } x -= dx ; y -= dy ; while ( this . isValid ( x , y ) && this . image . get ( x , y ) === color ) { x += dx ; } x -= dx ; while ( this . isValid ( x , y ) && this . image . get ( x , y ) === color ) { y += dy ; } y -= dy ; return new Point ( x , y ) ; } param cornerPoints param oldSide param newSide return private expandSquare ( cornerPoints , oldSide , newSide ) { let ratio = newSide / ( 2.0 * oldSide ) ; let dx = cornerPoints [ 0 ] . getX ( ) - cornerPoints [ 2 ] . getX ( ) ; let dy = cornerPoints [ 0 ] . getY ( ) - cornerPoints [ 2 ] . getY ( ) ; let centerx = ( cornerPoints [ 0 ] . getX ( ) + cornerPoints [ 2 ] . getX ( ) ) / 2.0 ; let centery = ( cornerPoints [ 0 ] . getY ( ) + cornerPoints [ 2 ] . getY ( ) ) / 2.0 ; let result0 = new ResultPoint ( centerx + ratio * dx , centery + ratio * dy ) ; let result2 = new ResultPoint ( centerx - ratio * dx , centery - ratio * dy ) ; dx = cornerPoints [ 1 ] . getX ( ) - cornerPoints [ 3 ] . getX ( ) ; dy = cornerPoints [ 1 ] . getY ( ) - cornerPoints [ 3 ] . getY ( ) ; centerx = ( cornerPoints [ 1 ] . getX ( ) + cornerPoints [ 3 ] . getX ( ) ) / 2.0 ; centery = ( cornerPoints [ 1 ] . getY ( ) + cornerPoints [ 3 ] . getY ( ) ) / 2.0 ; let result1 = new ResultPoint ( centerx + ratio * dx , centery + ratio * dy ) ; let result3 = new ResultPoint ( centerx - ratio * dx , centery - ratio * dy ) ; let results = [ result0 , result1 , result2 , result3 ] ; return results ; } private isValid ( x , y ) { return x >= 0 && x < this . image . getWidth ( ) && y > 0 && y < this . image . getHeight ( ) ; } private isValidPoint ( point ) { let x = MathUtils . round ( point . getX ( ) ) ; let y = MathUtils . round ( point . getY ( ) ) ; return this . isValid ( x , y ) ; } private distancePoint ( a , b ) { return MathUtils . distance ( a . getX ( ) , a . getY ( ) , b . getX ( ) , b . getY ( ) ) ; } private distanceResultPoint ( a , b ) { return MathUtils . distance ( a . getX ( ) , a . getY ( ) , b . getX ( ) , b . getY ( ) ) ; } private getDimension ( ) { if ( this . compact ) { return 4 * this . nbLayers + 11 ; } if ( this . nbLayers <= 4 ) { return 4 * this . nbLayers + 15 ; } return 4 * this . nbLayers + 2 * ( Integer . truncDivision ( ( this . nbLayers - 4 ) , 8 ) + 1 ) + 15 ; } }