2010-02-05 11:52:53 -08:00
using System ;
2013-01-18 12:14:03 -08:00
/ *
* Copyright 2009 ZXing authors
*
* 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 .
* /
2010-02-05 11:52:53 -08:00
namespace com.google.zxing.common.detector
{
2013-01-18 12:14:03 -08:00
using NotFoundException = com . google . zxing . NotFoundException ;
using ResultPoint = com . google . zxing . ResultPoint ;
using BitMatrix = com . google . zxing . common . BitMatrix ;
/// <summary>
/// <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
2010-02-05 11:52:53 -08:00
/// It looks within a mostly white region of an image for a region of black and white, but mostly
/// black. It returns the four corners of the region, as best it can determine.</p>
///
2013-01-18 12:14:03 -08:00
/// @author Sean Owen
2010-02-05 11:52:53 -08:00
/// </summary>
public sealed class MonochromeRectangleDetector
{
2013-01-18 12:14:03 -08:00
private const int MAX_MODULES = 32 ;
private readonly BitMatrix image ;
public MonochromeRectangleDetector ( BitMatrix image )
{
this . image = image ;
}
/// <summary>
/// <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
/// white, in an image.</p>
/// </summary>
/// <returns> <seealso cref="ResultPoint"/>[] describing the corners of the rectangular region. The first and
/// last points are opposed on the diagonal, as are the second and third. The first point will be
/// the topmost point and the last, the bottommost. The second point will be leftmost and the
/// third, the rightmost </returns>
/// <exception cref="NotFoundException"> if no Data Matrix Code can be found </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public com.google.zxing.ResultPoint[] detect() throws com.google.zxing.NotFoundException
public ResultPoint [ ] detect ( )
{
int height = image . Height ;
int width = image . Width ;
int halfHeight = height > > 1 ;
int halfWidth = width > > 1 ;
int deltaY = Math . Max ( 1 , height / ( MAX_MODULES < < 3 ) ) ;
int deltaX = Math . Max ( 1 , width / ( MAX_MODULES < < 3 ) ) ;
int top = 0 ;
int bottom = height ;
int left = 0 ;
int right = width ;
ResultPoint pointA = findCornerFromCenter ( halfWidth , 0 , left , right , halfHeight , - deltaY , top , bottom , halfWidth > > 1 ) ;
top = ( int ) pointA . Y - 1 ;
ResultPoint pointB = findCornerFromCenter ( halfWidth , - deltaX , left , right , halfHeight , 0 , top , bottom , halfHeight > > 1 ) ;
left = ( int ) pointB . X - 1 ;
ResultPoint pointC = findCornerFromCenter ( halfWidth , deltaX , left , right , halfHeight , 0 , top , bottom , halfHeight > > 1 ) ;
right = ( int ) pointC . X + 1 ;
ResultPoint pointD = findCornerFromCenter ( halfWidth , 0 , left , right , halfHeight , deltaY , top , bottom , halfWidth > > 1 ) ;
bottom = ( int ) pointD . Y + 1 ;
// Go try to find point A again with better information -- might have been off at first.
pointA = findCornerFromCenter ( halfWidth , 0 , left , right , halfHeight , - deltaY , top , bottom , halfWidth > > 2 ) ;
return new ResultPoint [ ] { pointA , pointB , pointC , pointD } ;
}
/// <summary>
/// Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
/// point which should be within the barcode.
/// </summary>
/// <param name="centerX"> center's x component (horizontal) </param>
/// <param name="deltaX"> same as deltaY but change in x per step instead </param>
/// <param name="left"> minimum value of x </param>
/// <param name="right"> maximum value of x </param>
/// <param name="centerY"> center's y component (vertical) </param>
/// <param name="deltaY"> change in y per step. If scanning up this is negative; down, positive;
/// left or right, 0 </param>
/// <param name="top"> minimum value of y to search through (meaningless when di == 0) </param>
/// <param name="bottom"> maximum value of y </param>
/// <param name="maxWhiteRun"> maximum run of white pixels that can still be considered to be within
/// the barcode </param>
/// <returns> a <seealso cref="com.google.zxing.ResultPoint"/> encapsulating the corner that was found </returns>
/// <exception cref="NotFoundException"> if such a point cannot be found </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: private com.google.zxing.ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right, int centerY, int deltaY, int top, int bottom, int maxWhiteRun) throws com.google.zxing.NotFoundException
private ResultPoint findCornerFromCenter ( int centerX , int deltaX , int left , int right , int centerY , int deltaY , int top , int bottom , int maxWhiteRun )
{
int [ ] lastRange = null ;
for ( int y = centerY , x = centerX ; y < bottom & & y > = top & & x < right & & x > = left ; y + = deltaY , x + = deltaX )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
int [ ] range ;
if ( deltaX = = 0 )
{
// horizontal slices, up and down
range = blackWhiteRange ( y , maxWhiteRun , left , right , true ) ;
}
else
{
// vertical slices, left and right
range = blackWhiteRange ( x , maxWhiteRun , top , bottom , false ) ;
}
if ( range = = null )
{
if ( lastRange = = null )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
throw NotFoundException . NotFoundInstance ;
}
// lastRange was found
if ( deltaX = = 0 )
{
int lastY = y - deltaY ;
if ( lastRange [ 0 ] < centerX )
{
if ( lastRange [ 1 ] > centerX )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
// straddle, choose one or the other based on direction
return new ResultPoint ( deltaY > 0 ? lastRange [ 0 ] : lastRange [ 1 ] , lastY ) ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
return new ResultPoint ( lastRange [ 0 ] , lastY ) ;
}
else
{
return new ResultPoint ( lastRange [ 1 ] , lastY ) ;
}
}
else
{
int lastX = x - deltaX ;
if ( lastRange [ 0 ] < centerY )
{
if ( lastRange [ 1 ] > centerY )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
return new ResultPoint ( lastX , deltaX < 0 ? lastRange [ 0 ] : lastRange [ 1 ] ) ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
return new ResultPoint ( lastX , lastRange [ 0 ] ) ;
}
else
{
return new ResultPoint ( lastX , lastRange [ 1 ] ) ;
}
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
}
lastRange = range ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
throw NotFoundException . NotFoundInstance ;
}
/// <summary>
/// Computes the start and end of a region of pixels, either horizontally or vertically, that could
/// be part of a Data Matrix barcode.
/// </summary>
/// <param name="fixedDimension"> if scanning horizontally, this is the row (the fixed vertical location)
/// where we are scanning. If scanning vertically it's the column, the fixed horizontal location </param>
/// <param name="maxWhiteRun"> largest run of white pixels that can still be considered part of the
/// barcode region </param>
/// <param name="minDim"> minimum pixel location, horizontally or vertically, to consider </param>
/// <param name="maxDim"> maximum pixel location, horizontally or vertically, to consider </param>
/// <param name="horizontal"> if true, we're scanning left-right, instead of up-down </param>
/// <returns> int[] with start and end of found range, or null if no such range is found
/// (e.g. only white was found) </returns>
private int [ ] blackWhiteRange ( int fixedDimension , int maxWhiteRun , int minDim , int maxDim , bool horizontal )
{
int center = ( minDim + maxDim ) > > 1 ;
// Scan left/up first
int start = center ;
while ( start > = minDim )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
if ( horizontal ? image . get ( start , fixedDimension ) : image . get ( fixedDimension , start ) )
{
start - - ;
}
else
{
int whiteRunStart = start ;
do
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
start - - ;
} while ( start > = minDim & & ! ( horizontal ? image . get ( start , fixedDimension ) : image . get ( fixedDimension , start ) ) ) ;
int whiteRunSize = whiteRunStart - start ;
if ( start < minDim | | whiteRunSize > maxWhiteRun )
{
start = whiteRunStart ;
break ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
}
}
start + + ;
// Then try right/down
int end = center ;
while ( end < maxDim )
{
if ( horizontal ? image . get ( end , fixedDimension ) : image . get ( fixedDimension , end ) )
{
end + + ;
}
else
{
int whiteRunStart = end ;
do
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
end + + ;
} while ( end < maxDim & & ! ( horizontal ? image . get ( end , fixedDimension ) : image . get ( fixedDimension , end ) ) ) ;
int whiteRunSize = end - whiteRunStart ;
if ( end > = maxDim | | whiteRunSize > maxWhiteRun )
{
end = whiteRunStart ;
break ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
}
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
end - - ;
return end > start ? new int [ ] { start , end } : null ;
}
2010-02-05 11:52:53 -08:00
}
}