iSightを使ってBooklog,MediaMarkerインポート用CSVファイルを生成するアプリ
Révision | deec2c46d1e4b89d37ea473242737695cab87712 |
---|---|
Taille | 72,759 octets |
l'heure | 2012-03-25 16:55:28 |
Auteur | masakih |
Message de Log | [Mod] PPCサポートをやめた
|
//
// MyScanner.m
// BarcodeScanner
//
// Created by Nora Schmitt on 12/4/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "MyScanner.h"
#import <MyGraphView.h>
#import <QTKit/QTKit.h>
#define BLACK_PIXEL 1
#define WHITE_PIXEL 0
//Not digit found value
#define NOT_FOUND -1
//How many bytes per pixel
//#define SAMPLES_PER_PIXEL_RGB 4
//#define SAMPLES_PER_PIXEL_YVU 2
//Number of lines to scan
#define NUMBER_OF_LINES_SCANNED 24
//Pixels between lines
#define SPACING_BETWEEN_SCAN_LINE 6
//offset of each center point on each line, starts in horizontal center.
#define SPACING_BETWEEN_CENTERS 2
//offset of each center point on each line, starts in horizontal center.
//#define NUMBER_OF_CENTERS 3
//The multiple of the average spacing that defines that a barcode area is over
#define MULTIPLY_AVERAGE_SPACING 3.5
#define MULTIPLY_AVERAGE_SPACING_OLD 4.0
#define RATIO_TO_CONSIDER_BOTTOM 0.2
//the roof limit of how sure of a digit value the algorythm can be. The lower the number the more volatile
#define SURENESS_LIMIT 10
// Minimum number of digits that need to be scan in one pass to consider the number worthy of adding to information present
#define MINIMUM_DIGITS_FOR_GOOD_SCAN 7
//At what value of the (read width / ideal single bar width) is a bar considered 4 bars wide.
#define SEPARATION_VALUE_FOR_3_BARS 3.7
//At what row to start scanning depending on the resoultion
//#define STARTING_LINE (bytesPerRow * (120 - (NUMBER_OF_LINES_SCANNED /2 * SPACING_BETWEEN_SCAN_LINE)))
//#define QUARTER_ROW_OFFSET (bytesPerRow / 4)
//#define BYTES_PER_ROW (SAMPLES_PER_PIXEL_YVU * width)
//#define FIRST_SCAN_LINE (BYTES_PER_ROW * ((height/2) - (NUMBER_OF_LINES_SCANNED /2 * SPACING_BETWEEN_SCAN_LINE)))
#ifdef DEBUG
// #define CLICK_TO_SCAN // Comment to have continous scanning during debug
// #define NUMBER_OF_LINES_SCANNED 1
#endif
//EAN encoding type
enum {
MKLeftHandEncodingOdd,
MKLeftHandEncodingBoth,
MKRightHandEncoding
};
@interface MyScanner (Private)
- (NSString *)scanBufferBackwards:(BOOL)backwards;
//- (void)getGreenFromRGB:(Ptr)rowPointer to:(int *)anArray640;
//- (void)getLuminanceFrom2vuy:(char *)rowPointer to:(int *)anArray640;
- (void)findStart:(int *)startBarcode end:(int *)endBarcode forLine:(int *)pixelLineArray derivative:(int *)lineDerivativeArray centerAt:(int)centerX min:(int *)minValue max:(int *)maxValue;
- (void)getTopMask:(int *)topMask bottomMask:(int *)bottomMask forLine:(int *)pixelLineArray start:(int)startBarcode end:(int)endBarcode;
- (void)getBars:(int [62])barsArrayOneDimension forLine:(int *)pixelLineArray derivative:(int *)lineDerivativeArray start:(int)startBarcode end:(int)endBarcode top:(int)topMask bottom:(int)bottomMask;
- (BOOL)getBinaryValueForPixel:(int)pixelValue derivativeForPixel:(int)derivative top:(int)topMask bottom:(int)bottomMask;
- (void)readBars:(int [62])lastBinaryData;
- (void)getNumberForLocation:(int *)anArray encoding:(int)encodingType location:(int)numberIndex;
int getNumberStripesEAN(int number, double average);
//- (void)readBarsBackward:(int [62])lastBinaryData;
- (void)clearNumberArray:(char [3][12])aNumberArray;
- (BOOL)calculateFirstNumberFromOddEvenParity:(char [7])aNumberArray;
- (BOOL)compareAgainstPreviousScans:(char [3][12])aNumberArray previous:(char [3][12])previousNumberArray;
- (NSString *)barcodeFromArray:(char [3][12])aNumberArray;
- (void)addNumber:(char [3][12])aNumberArray toFrequencyMatrix:(char [10][13])aFrequencyMatrix;
- (void)addParity:(char [3][12])aNumberArray toParityMatrix:(char[2][6])aFrequencyMatrix;
- (void)addFrequencyMatrix:(char[10][13])aFrequencyMatrix toFrequencyMatrix:(char[10][13])anotherFrequencyMatrix;
- (int)parityFromFrequency:(char[2][6])aParityMatrix;
- (BOOL)parityDigitFromNumber:(char [3][12])aNumberArray; //!!! can mix with the other functions
- (NSString *)numberFromFrequencyMatrix:(char [10][13])aFrequencyMatrix;
- (BOOL)numberFromFrequencyMatrix:(char [10][13])aFrequencyMatrix toArray:(char [13])anEANArray;
- (BOOL)isValidCheckDigit:(char [13])anEANArray;
- (NSString *)barcodeFromEANArray:(char [13])anEANArray;
@end
// Experimental and debug printing
#if DEBUG
@interface MyScanner (PrivateExperimental)
- (BOOL)checkCheckDigit:(char [3][12])aNumberArray;
- (void)printFrequencyMatrix:(char [10][13])aFrequencyMatrix;
- (void)printParityMatrix:(char[2][6])aParityMatrix;
- (NSString *)processPixelBufferOld:(CVPixelBufferRef)pixelBuffer;
- (void)findStartOld:(int *)startBarcode end:(int *)endBarcode forLine:(int *)pixelLineArray derivative:(int *)lineDerivativeArray centerAt:(int)centerX min:(int *)minValue max:(int *)maxValue;
- (void)getPeakDistanceForLine:(int *)pixelLineArray start:(int)startBarcode end:(int)endBarcode averageHeight:(int)averageHeight barArray:(int *)barsArray;
- (int)getNumberfromThickness:(int)thickness oneBar:(float)aBarWidth;
- (void)readBarsStartingWithHeaderBars:(int [62])lastBinaryData;
@end
#endif DEBUG
@implementation MyScanner
- (void) dealloc
{
[previousSingleFrameOldScan release];
[previewView release];
[super dealloc];
}
#pragma mark -
#pragma mark Barcode Scanning
//- (void)setHighResiSight:(BOOL)aBool {
//newHighResiSight = aBool;
/*if (newHighResiSight) {
int bytesPerRow = 1024 * 2; // FIRST_LINE_SCAN needs to know the bytes per row to calculate the offset
firstScanOffset = FIRST_LINE_SCAN_HIGH;
}
else {
//int bytesPerRow = 640 * 2; // FIRST_LINE_SCAN needs to know the bytes per row to calculate the offset
//firstScanOffset = FIRST_LINE_SCAN;
}
*/
//}
- (void)setPreviewView:(SampleCIView *)aView {
[previewView release];
previewView = [aView retain];
}
- (id)initWithDataBuffer:(unsigned char *)grayScale height:(NSInteger)heightOfImage width:(NSInteger)widthOfImage {
self = [super init];
if (self != nil) {
height = heightOfImage;
width = widthOfImage;
dataBuffer = grayScale;
firstScanOffset = (width * ((height/2) - (NUMBER_OF_LINES_SCANNED /2 * SPACING_BETWEEN_SCAN_LINE)));
// If we are in debug mode show a graph of the green pixel value
/*#if DEBUG
NSWindow *graphWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(30,30,640,400) styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];
[graphWindow setLevel: NSNormalWindowLevel];
[graphWindow setAlphaValue:1.00];
[graphWindow setOpaque:YES];
[graphWindow setHasShadow:NO];
graphView = [[[MyGraphView alloc] initWithFrame:[[graphWindow contentView] bounds]] autorelease];
[graphWindow setContentView:graphView];
[graphWindow makeKeyAndOrderFront:self];
NSWindow *barcodeWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(30,430,1280,100) styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];
[barcodeWindow setLevel: NSNormalWindowLevel];
[barcodeWindow setAlphaValue:1.00];
[barcodeWindow setOpaque:YES];
[barcodeWindow setHasShadow:NO];
barcodeView = [[[MyGraphView alloc] initWithFrame:[[barcodeWindow contentView] bounds]] autorelease];
[barcodeWindow setContentView:barcodeView];
[barcodeWindow makeKeyAndOrderFront:self];
#endif DEBUG
*/
}
return self;
}
- (NSString *)scan {
NSString *barcode = [self scanBufferBackwards:NO];
if (barcode)
return barcode;
barcode = [self scanBufferBackwards:YES];
return barcode;
}
- (NSString *)scanBufferBackwards:(BOOL)backwards {
int i, j;
int bottomMask, topMask;
int greenScalePixels[width]; //The value of the green pixel 0 - 255
int greenDerivative[width];
int xAxisCenterPoint = (width/2) - (NUMBER_OF_LINES_SCANNED / 2 * SPACING_BETWEEN_CENTERS); //initalize x center point
//[previewView setGoodScan:NO];
BOOL noMissingNumbers = NO; //check local number as all digits where deciphered
BOOL noMissingNumbersOLD = NO;
//clear local number
for (i = 0; i < 12; i++) {
previousNumberLocalArray[0][i] = NOT_FOUND;
previousNumberLocalArray[2][i] = 0;
previousNumberLocalArrayOLD[0][i] = NOT_FOUND;
previousNumberLocalArrayOLD[2][i] = 0;
}
previousNumberLocalArray[1][6] = NOT_FOUND;
previousNumberLocalArrayOLD[1][6] = NOT_FOUND;
char frequencyMatrix[10][13] = {0};
char parityFrequencyMatrix[2][6] = {0};
//clear
// Should clear every 90 frames or so
//if (!backwards) {
if (frameCount == 180) { //It's really 90 frmaes, but we scan twice. Forwards and backwards
[self clearGlobalFrequency];
//NSLog(@"CLEAR GLOBAL");
}
frameCount++;
//}
// http://www.cocoabuilder.com/archive/message/cocoa/2007/12/15/194970
//NSLog(@"Scan lines");
//Do a number of rows from the same image centered in the middle with varying x axis center points
for (i = 0; i < NUMBER_OF_LINES_SCANNED; i++) {
NSUInteger offset = firstScanOffset + (i * width * SPACING_BETWEEN_SCAN_LINE) ;
//!!! If I can flip all the value functions I can remove this step
if (backwards) {
for (j = 0; j < width; j++) {
greenScalePixels[j] = 255 - dataBuffer[offset + width - j];
}
}
else {
for (j = 0; j < width; j++) {
greenScalePixels[j] = 255 - dataBuffer[offset + j];
}
}
for (j = 0; j < width; j++) {
greenDerivative[j] = greenScalePixels[j + 1] - greenScalePixels[j];
}
xAxisCenterPoint = xAxisCenterPoint + SPACING_BETWEEN_CENTERS;
//NSLog(@"Find barcode area");
//Find barcode area and information about max and min values of the pixels
int startBarcode = 0, endBarcode = width -1, minValue = 255, maxValue = 0;
[self findStart:&startBarcode end:&endBarcode forLine:greenScalePixels derivative:greenDerivative centerAt:xAxisCenterPoint min:&minValue max:&maxValue];
/*
#ifdef DEBUG
if (i == 0) {
//Draw a green line of the green pixel values
NSBezierPath *fullWave = [NSBezierPath bezierPath];
[fullWave moveToPoint:NSMakePoint(0, dataBuffer[offset])];
for (j = 1; j < width; j++)
[fullWave lineToPoint:NSMakePoint(j, dataBuffer[offset + j])];
[graphView setGreenPath:fullWave];
//Draw a blue line for the area of the barcode
NSBezierPath *barcodeAreaPath = [NSBezierPath bezierPath];
[barcodeAreaPath moveToPoint:NSMakePoint(startBarcode, dataBuffer[offset + startBarcode])];
for (j = startBarcode + 1; j < endBarcode; j++)
[barcodeAreaPath lineToPoint:NSMakePoint(j, dataBuffer[offset + j])];
[graphView setBluePath:barcodeAreaPath];
NSBezierPath *derivativePath = [NSBezierPath bezierPath];
[derivativePath moveToPoint:NSMakePoint(startBarcode, dataBuffer[offset + startBarcode] + 280) ];
for (j = startBarcode + 1; j < endBarcode; j++) {
[derivativePath lineToPoint:NSMakePoint(j, greenDerivative[j] + 280)];
}
[graphView setPath:derivativePath withColor:[NSColor orangeColor]];
NSBezierPath *derivativePathZero = [NSBezierPath bezierPath];
[derivativePathZero moveToPoint:NSMakePoint(startBarcode, 280) ];
[derivativePathZero lineToPoint:NSMakePoint(endBarcode, 280)];
[graphView setPath:derivativePathZero withColor:[NSColor yellowColor]];
}
#endif
*/
// A real barcode will not reach to the edge of the frame
if (endBarcode != width -1 && startBarcode > 0) {
int barsArray[62] = {0};
//NSLog(@"Get mask");
[self getTopMask:&topMask bottomMask:&bottomMask forLine:greenScalePixels start:startBarcode end:endBarcode];
//Get the bar width information based on the mask
// This function can do with a serious improvement, it only likes very steep and condensed barcodes, if a barcode has soft curves it fails, as it uses the derivative to decide if a pizel is black or white insteasd of an average tone between the last two peaks.
//NSLog(@"Bars");
//NSLog(@"%d %d %d %d ", bottomMask, topMask, startBarcode, endBarcode);
[self getBars:barsArray forLine:greenScalePixels derivative:greenDerivative start:startBarcode end:endBarcode top:topMask bottom:bottomMask];
//Try to read a number based on the bars widths
numberOfDigitsFound = 0;
[self clearNumberArray:numberArray];
//[self readBars:barsArray];
//NSLog(@"Read bars");
[self readBars:barsArray];
//NSLog(@"Process bars");
if (numberOfDigitsFound > MINIMUM_DIGITS_FOR_GOOD_SCAN ) {
[previewView setGoodScan:YES];
[self calculateFirstNumberFromOddEvenParity:numberArray[1]];
//if (!backwards) {
[self addNumber:numberArray toFrequencyMatrix:frequencyMatrix];
[self addParity:numberArray toParityMatrix:parityFrequencyMatrix];
//}
noMissingNumbers = [self compareAgainstPreviousScans:numberArray previous:previousNumberLocalArray];
}
//NSLog(@"Scanned %@ local:%@ j:%d", [self barcodeFromArray:numberArray], [self barcodeFromArray:previousNumberLocalArray], j);
#if DEBUG
//NSString *forwardScan = [self barcodeFromArray:numberArray];
#endif
//Try the old version on this line as well
// if the end goes to the edge of the frame do not process
//if (endBarcode != width -1) {
int differenceInRange = maxValue - minValue;
// repeat for different values of the masks
// the masks were determined via trial and error
for (j =0; j < 3; j++) {
int barsArray[62] = {0};
if (j == 0) {
bottomMask = minValue + (differenceInRange * 0.34);
topMask = maxValue - (differenceInRange * 0.44);
}
else if (j == 1) {
bottomMask = minValue + (differenceInRange * 0.15);
topMask = maxValue - (differenceInRange * 0.4);
}
/*else if (j == 2) {
bottomMask = minValue + (differenceInRange * 0.3);
topMask = maxValue - (differenceInRange * 0.3);
}
else {
bottomMask = minValue + (differenceInRange * 0.1);
topMask = maxValue - (differenceInRange * 0.25);
}
*/
else {
bottomMask = minValue + (differenceInRange * 0.18);
topMask = maxValue - (differenceInRange * 0.5);
}
//NSLog(@"%d %d %d %d ", bottomMask, topMask, startBarcode, endBarcode);
//NSLog(@"Get bars old");
//Get the bar width information based on the mask
[self getBars:barsArray forLine:greenScalePixels derivative:greenDerivative start:startBarcode end:endBarcode top:topMask bottom:bottomMask];
//NSLog(@"bars %@", [self stringFromBars:barsArray]);
//Try to read a number based on the bars widths
numberOfDigitsFound = 0;
[self clearNumberArray:numberArray];
//NSLog(@"read bars old");
[self readBars:barsArray];
//NSLog(@"process bars old");
// If 7 or more digits were read from the barcode then process number
// and add it to the local number
// Don't check the scanned number if it has 12 digits as it not verfied and it could lead to a lucky checksum and a wrong number
if (numberOfDigitsFound >= MINIMUM_DIGITS_FOR_GOOD_SCAN) {
//NSLog(@"Scanned %@ local:%@ j:%d", [self barcodeFromArray:numberArray], [self barcodeFromArray:previousNumberLocalArrayOLD], j);
//NSLog(@"j = %d %d", j, numberOfDigitsFound);
//Also add to frequency
//[self calculateFirstNumberFromOddEvenParity:numberArray[1]];
//[self addNumber:numberArray toFrequencyMatrix:frequencyMatrix];
//[self addParity:numberArray toParityMatrix:parityFrequencyMatrix];
//[vide setGoodScan:YES];
[previewView setGoodScan:YES];
noMissingNumbersOLD = [self compareAgainstPreviousScans:numberArray previous:previousNumberLocalArrayOLD];
//[vide setScannedNumber:[self barcodeFromArray:previousNumberGlobalArray]];
}
} // old section
} // 639 section
//[pool release];
//} //centers
}
int parityBasedOnFrequency = [self parityFromFrequency:parityFrequencyMatrix];
if (parityBasedOnFrequency != NOT_FOUND)
frequencyMatrix[parityBasedOnFrequency][0] += 2; //Weighted by 2 as it's from the frequency and not a single scan
char localEANArray[13];
if ([self numberFromFrequencyMatrix:frequencyMatrix toArray:localEANArray]) {
if ([self isValidCheckDigit:localEANArray]) {
NSString *barcodeString = [self barcodeFromEANArray:localEANArray];
//NSLog(@"Local frequency: %@", barcodeString);
//[pool release];
return barcodeString; //Otherwise it might sent another message with the global matches as well
}
}
if (!backwards) {
[self addFrequencyMatrix:frequencyMatrix toFrequencyMatrix:globalFrequencyMatrix];
char globalEANArray[13];
if ([self numberFromFrequencyMatrix:globalFrequencyMatrix toArray:globalEANArray]) {
if ([self isValidCheckDigit:globalEANArray]) {
NSString *barcodeString = [self barcodeFromEANArray:globalEANArray];
//NSLog(@"Global frequency: %@", barcodeString);
//[pool release];
return barcodeString; //Otherwise it might sent another message with the global matches as well
}
}
}
//check the local number if it has no missing numbers run the checksum
if (noMissingNumbersOLD) {
if ([self parityDigitFromNumber:previousNumberLocalArrayOLD]) {
//Is correct but the single frame old, is sometimes mistaken s, has to scan the number twice
NSString *barcodeString = [self barcodeFromArray:previousNumberLocalArrayOLD];
if ([barcodeString isEqualToString:previousSingleFrameOldScan]) {
[previousSingleFrameOldScan release]; previousSingleFrameOldScan = nil;
//NSLog(@"Single frame old version: %@", barcodeString);
return barcodeString;
}
[previousSingleFrameOldScan release];
previousSingleFrameOldScan = [barcodeString retain];
}
}
//check the local number if it has no missing numbers run the checksum
if (noMissingNumbers) {
if ([self parityDigitFromNumber:previousNumberLocalArray]) {
NSString *barcodeString = [self barcodeFromArray:previousNumberLocalArray];
if (barcodeString) {
//NSLog(@"Single local frame: %@", barcodeString);
return barcodeString;
}
}
}
return nil;
}
/*
- (void)getLuminanceFrom2vuy:(char *)rowPointer to:(int *)anArray640 {
#warning remove
//Optimized version of the one below
unsigned int offset = 1; // Plus one as we want luminace pixel V in YUV
unsigned char prevPixel = 0, nextPixel;
unsigned char mainPixel = (unsigned char) *(rowPointer + offset);
int i;
for (i = 0; i < 640; i++) {
offset += 2;
nextPixel = (unsigned char) *(rowPointer + offset);
anArray640[i] = (mainPixel * 3 + prevPixel * -1 + nextPixel * -1)/1;
prevPixel = mainPixel;
mainPixel = nextPixel;
}
//int i;
//for (i = 0; i < 640; i++) {
// anArray640[i] = 255 - (UInt8)*(rowPointer + (i * 2) + 1) ;
//}
}
- (void)getLuminanceFrom2vuyBackwards:(char *)rowPointer to:(int *)anArray640 {
int i;
for (i = 0; i < 640; i++) {
anArray640[i] = 255 - (UInt8)*(rowPointer + ((639 - i) * 2) + 1) ;
}
}
//Get the Green pixel value for a row from the Pixel buffer
- (void)getGreenFromRGB:(Ptr)rowPointer to:(int *)anArray640 {
// mirroring done by the CIImage at the display level
int i;
for (i = 0; i < 640; i++) {
anArray640[i] = (UInt8)*(rowPointer + (i * SAMPLES_PER_PIXEL_RGB) +2) ;
anArray640[i] = 255 - anArray640[i];
}
}
*/
#define NOISE_REDUCTION 4
// Determine the area of the barcode by using the supplied center and determining the average change of direction
// As soon as something is below 1/4 of the variation in height for more than the average spacing time MULTIPLY_AVERAGE_SPACING, then the barcode ends
- (void)findStart:(int *)startBarcode end:(int *)endBarcode forLine:(int *)pixelLineArray derivative:(int *)lineDerivativeArray centerAt:(int)centerX min:(int *)minValue max:(int *)maxValue {
int averageSpacing = 0, numberOfCurves = 0, spacingInThisRun = 0;
int i, count, startScan = centerX - 40, endScan = centerX + 40;
BOOL positive = YES;
if (lineDerivativeArray[startScan] < 0)
positive = NO;
//Build the average spacing number and the variation in height from a sample around the center
for (i = startScan; i < endScan; i++) {
if (*maxValue < pixelLineArray[i])
*maxValue = pixelLineArray[i];
else if (*minValue > pixelLineArray[i])
*minValue = pixelLineArray[i];
if (lineDerivativeArray[i] < -NOISE_REDUCTION) {
if (positive) {
positive = NO;
averageSpacing = averageSpacing + spacingInThisRun;
numberOfCurves++;
spacingInThisRun = 0;
}
}
else if (lineDerivativeArray[i] > NOISE_REDUCTION){
if (!positive) {
positive = YES;
averageSpacing = averageSpacing + spacingInThisRun;
numberOfCurves++;
spacingInThisRun = 0;
}
}
spacingInThisRun++;
}
//If there was a solid growing gradient with no curves then return
if (numberOfCurves == 0)
return;
int localStartBarcode = 0, localEndBarcode = width -1;
averageSpacing = (averageSpacing / numberOfCurves) * MULTIPLY_AVERAGE_SPACING;
//NSLog(@"min %d max %d spacing: %d curves: %d", *minValue, *maxValue, averageSpacing, numberOfCurves);
int quarterHeight = ((*maxValue - *minValue) * RATIO_TO_CONSIDER_BOTTOM);
int bottomForth = *minValue + quarterHeight;
int topForth = *maxValue - quarterHeight;
//anything below oneForth for averageSpacing * MULTIPLY_AVERAGE_SPACING is the end of the barcode
for (i = centerX, count = 0; i > 0; i--) {
if (pixelLineArray[i] < bottomForth ) { //|| pixelLineArray[i] > topForth) { //We need the beginnign to be exact, but not the end
count++;
}
else {
count = 0;
localStartBarcode = i;
}
if (count > averageSpacing) {
*startBarcode = localStartBarcode;
break;
}
}
for (i = centerX, count = 0; i < width; i++) {
if (pixelLineArray[i] < bottomForth || pixelLineArray[i] > topForth) {
count++;
}
else {
count = 0;
localEndBarcode = i;
}
if (count > averageSpacing) {
*endBarcode = localEndBarcode;
break;
}
}
//NSLog(@"beginning: %d end %d", *startBarcode, *endBarcode);
}
- (void)getTopMask:(int *)topMask bottomMask:(int *)bottomMask forLine:(int *)pixelLineArray start:(int)startBarcode end:(int)endBarcode{
*bottomMask = 255; //lowestPeak
*topMask = 0; //highestValley
BOOL lookformax = YES;
int mn = 256, mx = -256;
//int mnpos, mxpos;
int delta = 0;
int index = startBarcode;
int peakNumber = 0;
//int i;
while (index < endBarcode && peakNumber < 69) {
int current = pixelLineArray[index];
if (current > mx) {
mx = current;
//mxpos = index;
}
if (current < mn) {
mn = current;
//mnpos = index;
}
if (lookformax) {
//peak
if (current < (mx - delta)) {
if (current < *bottomMask)
*bottomMask = current;
lookformax = NO;
mn = current;
//mnpos = index;
}
}
else {
//valley
if (current > (mn + delta)) {
if (current > *topMask)
*topMask = current;
mx = current;
//mxpos = index;
lookformax = YES;
}
}
index++;
}
*--bottomMask;
*++topMask;
}
//given an array of pixel values and the first derivative as well as the area of the barcode it goes throw determining the bar lengths.
- (void)getBars:(int [62])barsArrayOneDimension forLine:(int *)pixelLineArray derivative:(int *)lineDerivativeArray start:(int)startBarcode end:(int)endBarcode top:(int)topMask bottom:(int)bottomMask {
//NSLog(@"%d %d %d %d" ,startBarcode, endBarcode, topMask, bottomMask);
float barWidths[70];
int i, sectionThickness = 0;
BOOL blackSection = YES;
int nextSection = 0;
BOOL binaryValueOfPixel;
float barWidth = (endBarcode - startBarcode) / 96.0; //What a single bar width should be
#if DEBUG
//NSBezierPath *blackPath = [NSBezierPath bezierPath];
#endif
//For the entire area of the barcode
for (i = startBarcode; i < endBarcode && nextSection < 62; i++) {
//NSLog(@"%d %d" ,i, nextSection);
// Determine the binaryValue of the pixel
binaryValueOfPixel = [self getBinaryValueForPixel:pixelLineArray[i] derivativeForPixel:lineDerivativeArray[i] top:topMask bottom:bottomMask];
//black pixel (is really a white pixel and it's counting backwards, but since it does not label the pixel color it does not matter)
if (binaryValueOfPixel == NO) {
// We are not in a black section, determine the previous white bar width
if (!blackSection) {
//Get bar width
int stripes = getNumberStripesEAN(sectionThickness, barWidth);
barWidths[nextSection] = sectionThickness;
barsArrayOneDimension[nextSection] = stripes;
nextSection++;
sectionThickness = 0;
}
//set black section
blackSection = YES;
sectionThickness++;
}
//White pixel
else {
#if DEBUG
//[blackPath moveToPoint:NSMakePoint(i, 255)];
//[blackPath lineToPoint:NSMakePoint(i, 0)];
#endif
//We are not in a white section, determine the previous black bar width
if (blackSection) {
//Get bar width
int stripes = getNumberStripesEAN(sectionThickness, barWidth);
barWidths[nextSection] = sectionThickness;
barsArrayOneDimension[nextSection] = stripes;
nextSection++;
sectionThickness = 0;
}
//set white section
blackSection = NO;
sectionThickness++;
}
}
/*
#if DEBUG
//Draw a barcode by averaging the spacing between the peaks and the valleys
[barcodeView setPath:blackPath withColor:[NSColor blackColor]];
[barcodeView display];
#endif
*/
}
// Determines if a pixel is black or white depending on the mask provided and if not on the first derivative
// A possability is changing the first derivative to the second derivative to be more precise about the midpoint
// or even a simple midpoint calculation between the peak and the valley
- (BOOL)getBinaryValueForPixel:(int)pixelValue derivativeForPixel:(int)derivative top:(int)topMask bottom:(int)bottomMask {
if (pixelValue > topMask) {
return YES;
}
else if (pixelValue < bottomMask) {
return NO;
}
else {
//use derivative
if (derivative < 0) {
return YES;
}
else {
return NO;
}
}
}
#pragma mark -
//given a array of bar thickness it read four bars at a time and determines the number based on the encoding
- (void)readBars:(int [62])lastBinaryData {
int k, i;
if (lastBinaryData[0] == 1 && lastBinaryData[1] == 1 && lastBinaryData[2] == 1 && lastBinaryData[3] == 1) {
i = 4;
/*}
else {
i = 3;
}
*/
//First number has to be odd encoded
[self getNumberForLocation:&lastBinaryData[i] encoding:MKLeftHandEncodingOdd location:0];
if (numberArray[0][0] != NOT_FOUND)
numberOfDigitsFound++;
//First Section left hand encoding even or odd
for (i = i +4, k = 1; i < 28; i = i + 4, k++) {
[self getNumberForLocation:&lastBinaryData[i] encoding:MKLeftHandEncodingBoth location:k];
if (numberArray[0][k] != NOT_FOUND)
numberOfDigitsFound++;
}
//Second section all right hand encoding
for (i = i+5; i < 57; i = i + 4, k++) {
[self getNumberForLocation:&lastBinaryData[i] encoding:MKRightHandEncoding location:k];
if (numberArray[0][k] != NOT_FOUND)
numberOfDigitsFound++;
}
} /*
else {
//[self clearNumberArray:numberArray];
}
*/
}
//given a array of bar thickness it read four bars at a time and determines the number based on the encoding
- (void)readBarsWithShift:(int [62])lastBinaryData {
int i, k;
//Starts with 0101
//find starter bar 1111 should be in the first 7
for (i = 0; i < 5; i++) {
if (lastBinaryData[i] == 1 && lastBinaryData[i+1] == 1 && lastBinaryData[i+2] == 1 && lastBinaryData[i+3] != 1) {
break;
}
}
//NSLog(@"Begin Position: %d", i);
if (i < 5) {
i = i + 4;
//First number has to be odd encoded
[self getNumberForLocation:&lastBinaryData[i] encoding:MKLeftHandEncodingOdd location:0];
if (numberArray[0][0] != NOT_FOUND)
numberOfDigitsFound++;
//First Section left hand encoding even or odd
for (i = i +4, k = 1; i < 28; i = i + 4, k++) {
[self getNumberForLocation:&lastBinaryData[i] encoding:MKLeftHandEncodingBoth location:k];
if (numberArray[0][k] != NOT_FOUND)
numberOfDigitsFound++;
}
//Second section all right hand encoding
for (i = i+5; i < 57; i = i + 4, k++) {
[self getNumberForLocation:&lastBinaryData[i] encoding:MKRightHandEncoding location:k];
if (numberArray[0][k] != NOT_FOUND)
numberOfDigitsFound++;
}
} /*
else {
//[self clearNumberArray:numberArray];
}
*/
}
/*
- (void)readBarsBackward:(int [62])lastBinaryData {
int i, k;
//find starter bar at back
for (i = 61; i > 54; i--) {
if ((lastBinaryData[i-3] == 1 && lastBinaryData[i-2] == 1 && lastBinaryData[i] == 0) || (lastBinaryData[i-3] == 1 && lastBinaryData[i-2] == 1 && lastBinaryData[i -1] == 1)) {
break;
}
}
i = i - 3;
//NSLog(@"footer Position: %d", i);
if (i > 51) {
//Second section all right hand encoding
for (i = i - 4, k = 11; i > 0 && k > 5; i = i - 4, k--) {
[self getNumberForLocation:&lastBinaryData[i] encoding:MKRightHandEncoding location:k];
if (numberArray[0][k] != NOT_FOUND)
numberOfDigitsFound++;
}
}
}
*/
// Given an array of bar lengths an index to begin the scan, it scans the next four bars and determines the number
// based on the UPC/EAN encoding
// For more info check out: http://www.barcodeisland.com/ean13.phtml
- (void)getNumberForLocation:(int *)anArray encoding:(int)encodingType location:(int)numberIndex {
//All 6 numbers on the right hand of the code have a single encoding
if (encodingType == MKRightHandEncoding) {
//numberArray[1][numberIndex] = 0;
if (anArray[0] == 3 && anArray[1] == 2 && anArray[2] == 1 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 0;
return;
}
else if (anArray[0] == 2 && anArray[1] == 2 && anArray[2] == 2 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 1;
return;
}
else if (anArray[0] == 2 && anArray[1] == 1 && anArray[2] == 2 && anArray[3] == 2)
{
numberArray[0][numberIndex] = 2;
return;
}
else if (anArray[0] == 1 && anArray[1] == 4 && anArray[2] == 1 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 3;
return;
}
else if (anArray[0] == 1 && anArray[1] == 1 && anArray[2] == 3 && anArray[3] == 2)
{
numberArray[0][numberIndex] = 4;
return;
}
else if (anArray[0] == 1 && anArray[1] == 2 && anArray[2] == 3 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 5;
return;
}
else if (anArray[0] == 1 && anArray[1] == 1 && anArray[2] == 1 && anArray[3] == 4)
{
numberArray[0][numberIndex] = 6;
return;
}
else if (anArray[0] == 1 && anArray[1] == 3 && anArray[2] == 1 && anArray[3] == 2)
{
numberArray[0][numberIndex] = 7;
return;
}
else if (anArray[0] == 1 && anArray[1] == 2 && anArray[2] == 1 && anArray[3] == 3)
{
numberArray[0][numberIndex] = 8;
return;
}
else if (anArray[0] == 3 && anArray[1] == 1 && anArray[2] == 1 && anArray[3] == 2)
{
numberArray[0][numberIndex] = 9;
return;
}
numberArray[0][numberIndex] = NOT_FOUND;
return;
}
//the first 6 numbers on the left hand has two encodings that allow it to determine the 13 number for EAN numbers
//odd parity
if (anArray[0] == 3 && anArray[1] == 2 && anArray[2] == 1 && anArray[3] == 1) //isEqualToString:@"0001101"])
{
numberArray[0][numberIndex] = 0;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 2 && anArray[1] == 2 && anArray[2] == 2 && anArray[3] == 1) //isEqualToString:@"0011001"])
{
numberArray[0][numberIndex] = 1;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 2 && anArray[1] == 1 && anArray[2] == 2 && anArray[3] == 2) //isEqualToString:@"0010011"])
{
numberArray[0][numberIndex] = 2;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 1 && anArray[1] == 4 && anArray[2] == 1 && anArray[3] == 1) //isEqualToString:@"0111101"])
{
numberArray[0][numberIndex] = 3;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 1 && anArray[1] == 1 && anArray[2] == 3 && anArray[3] == 2) //isEqualToString:@"0100011"])
{
numberArray[0][numberIndex] = 4;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 1 && anArray[1] == 2 && anArray[2] == 3 && anArray[3] == 1) //isEqualToString:@"0110001"])
{
numberArray[0][numberIndex] = 5;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 1 && anArray[1] == 1 && anArray[2] == 1 && anArray[3] == 4) //isEqualToString:@"0101111"])
{
numberArray[0][numberIndex] = 6;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 1 && anArray[1] == 3 && anArray[2] == 1 && anArray[3] == 2) //isEqualToString:@"0111011"])
{
numberArray[0][numberIndex] = 7;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 1 && anArray[1] == 2 && anArray[2] == 1 && anArray[3] == 3) //isEqualToString:@"0110111"])
{
numberArray[0][numberIndex] = 8;
numberArray[1][numberIndex] = 1;
return;
}
else if (anArray[0] == 3 && anArray[1] == 1 && anArray[2] == 1 && anArray[3] == 2) //isEqualToString:@"0001011"])
{
numberArray[0][numberIndex] = 9;
numberArray[1][numberIndex] = 1;
return;
}
//even parity
if (encodingType == MKLeftHandEncodingBoth) {
if (anArray[0] == 1 && anArray[1] == 1 && anArray[2] == 2 && anArray[3] == 3)
{
numberArray[0][numberIndex] = 0;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 1 && anArray[1] == 2 && anArray[2] == 2 && anArray[3] == 2)
{
numberArray[0][numberIndex] = 1;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 2 && anArray[1] == 2 && anArray[2] == 2 && anArray[3] == 2)
{
numberArray[0][numberIndex] = 2;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 1 && anArray[1] == 1 && anArray[2] == 4 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 3;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 2 && anArray[1] == 3 && anArray[2] == 1 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 4;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 1 && anArray[1] == 3 && anArray[2] == 2 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 5;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 4 && anArray[1] == 1 && anArray[2] == 1 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 6;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 2 && anArray[1] == 1 && anArray[2] == 3 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 7;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 3 && anArray[1] == 1 && anArray[2] == 2 && anArray[3] == 1)
{
numberArray[0][numberIndex] = 8;
numberArray[1][numberIndex] = 0;
return;
}
else if (anArray[0] == 2 && anArray[1] == 1 && anArray[2] == 1 && anArray[3] == 3)
{
numberArray[0][numberIndex] = 9;
numberArray[1][numberIndex] = 0;
return;
}
}
// Code could be added here if a number is not found, then check the neighbours and see if they include a large white or black bar that might
// throw the bar thickness of for this section and correct for that error
numberArray[0][numberIndex] = NOT_FOUND;
}
//Given a number of consecutive equal binary values and the average thickness of single bar it returns the thickness of the bar
int getNumberStripesEAN(int number, double average) {
double ratioToAverageThickness = number / average;
if (ratioToAverageThickness < 1.5)
return 1;
else if (ratioToAverageThickness < 2.5)
return 2;
else if (ratioToAverageThickness < SEPARATION_VALUE_FOR_3_BARS)
return 3;
else
return 4;
}
- (BOOL)calculateFirstNumberFromOddEvenParity:(char [7])aParitySection {
#if DEBUG
//NSLog(@"%d %d %d %d %d %d" , aParitySection[0], aParitySection[1], aParitySection[2], aParitySection[3], aParitySection[4], aParitySection[5]);
//NSLog(@"%d", aParitySection[6]);
#endif DEBUG
//first build the first number from odd even parity and store it index 6 of the odd even array [1][6]
if (aParitySection[1] == 1) { // odd
if (aParitySection[2] == 1) { // odd
if (aParitySection[3] == 1 || aParitySection[4] == 1 || aParitySection[5] == 1) //odd odd odd
aParitySection[6] = 0; //12 digit UPC first number is 0
else
return NO;
}
else if (aParitySection[2] == 0) { // odd even
if (aParitySection[3] == 1) { // odd
if (aParitySection[4] == 0 || aParitySection[5] == 0) //even even
aParitySection[6] = 1;
else
return NO;
}
else if (aParitySection[3] == 0) { //odd even even
if (aParitySection[4] == 1) { // odd
if (aParitySection[5] == 0) //even
aParitySection[6] = 2;
else
return NO;
}
else if (aParitySection[4] == 0) {// even
if (aParitySection[5] == 1) //odd
aParitySection[6] = 3;
else
return NO;
}
}
}
}
else if (aParitySection[1] == 0) {
if (aParitySection[2] == 1) { // even odd
if (aParitySection[3] == 1) { //odd
if (aParitySection[4] == 0 || aParitySection[5] == 0) //even even
aParitySection[6] = 4;
else
return NO;
}
else if (aParitySection[3] == 0) {
if (aParitySection[4] == 1) { // even odd even odd
if (aParitySection[5] == 0) //even
aParitySection[6] = 7;
else
return NO;
}
else if (aParitySection[4] == 0) {// even odd even even
if (aParitySection[5] == 1) //odd
aParitySection[6] = 8;
else
return NO;
}
}
}
else if (aParitySection[2] == 0) {
if (aParitySection[3] == 1) {
if (aParitySection[4] == 1) { // even even odd odd
if (aParitySection[5] == 0) // even
aParitySection[6] = 5;
else
return NO;
}
else if (aParitySection[4] == 0) {// even even odd even
if (aParitySection[5] == 1) //odd
aParitySection[6] = 9;
else
return NO;
}
}
else if (aParitySection[3] == 0) {
// even even even
if (aParitySection[4] == 1 || aParitySection[5] == 1) //odd odd
aParitySection[6] = 6;
else
return NO;
}
}
}
#if DEBUG
//NSLog(@"%d", aParitySection[6]);
#endif DEBUG
return YES;
}
- (BOOL)parityDigitFromNumber:(char [3][12])aNumberArray {
//first build the first number from odd even parity and store it index 6 of the odd even array [1][6]
if (aNumberArray[1][1] == 1) {
if (aNumberArray[1][2] == 1) { // odd odd
aNumberArray[1][6] = 0; //12 digit UPC first number is 0
if (aNumberArray[1][3] != 1 || aNumberArray[1][4] != 1 || aNumberArray[1][5] != 1) //check the rest
return NO;
}
else {
if (aNumberArray[1][3] == 1) {
aNumberArray[1][6] = 1; // odd even odd
if (aNumberArray[1][4] != 0 || aNumberArray[1][5] != 0) //check the rest
return NO;
}
else {
if (aNumberArray[1][4] == 1) {
aNumberArray[1][6] = 2; // odd even even odd
if (aNumberArray[1][5] != 0) //check the rest
return NO;
}
else {
aNumberArray[1][6] = 3; // odd even even even
if (aNumberArray[1][5] != 1) //check the rest
return NO;
}
}
}
}
else {
if (aNumberArray[1][2] == 1) { // even odd
if (aNumberArray[1][3] == 1) {
aNumberArray[1][6] = 4; // even odd odd
if (aNumberArray[1][4] != 0 || aNumberArray[1][5] != 0) //check the rest
return NO;
}
else {
if (aNumberArray[1][4] == 1) {
aNumberArray[1][6] = 7; // even odd even odd
if (aNumberArray[1][5] != 0) //check the rest
return NO;
}
else {
aNumberArray[1][6] = 8; // even odd even even
if (aNumberArray[1][5] != 1) //check the rest
return NO;
}
}
}
else {
if (aNumberArray[1][3] == 1) {
if (aNumberArray[1][4] == 1) {
aNumberArray[1][6] = 5; // even even odd odd
if (aNumberArray[1][5] != 0) //check the rest
return NO;
}
else {
aNumberArray[1][6] = 9; // even even odd even
if (aNumberArray[1][5] != 1) //check the rest
return NO;
}
}
else {
aNumberArray[1][6] = 6; // even even even
if (aNumberArray[1][4] != 1 || aNumberArray[1][5] != 1) //check the rest
return NO;
}
}
}
//check digit
// the first digit is in [1][6]
// 11 total count becasue we want to leave the check digit out of the calculation
int i, checkDigitSum = aNumberArray[1][6];
for (i = 0; i < 11; i++) {
if ( i % 2)
checkDigitSum = checkDigitSum + aNumberArray[0][i];
else
checkDigitSum = checkDigitSum + (aNumberArray[0][i] * 3);
}
//NSLog(@"check Sum %d", checkDigitSum);
checkDigitSum = 10 - (checkDigitSum % 10);
//NSLog(@"check %d == %d", checkDigitSum, aNumberArray[0][11]);
//OLD WAY if (checkDigitSum == aNumberArray[0][11] || (checkDigitSum == 10 && aNumberArray[0][11] == 0))
checkDigitSum = checkDigitSum % 10;
if (checkDigitSum == aNumberArray[0][11])
return YES;
return NO;
}
// Compares two barcode numbers and merges them intelligently. That way not wasting previous scans. It keeps a table of how sure it is about a number depending on how many times that number has appeared at that location
- (BOOL)compareAgainstPreviousScans:(char [3][12])aNumberArray previous:(char [3][12])previousNumberArray {
int i;
BOOL completeNumber = YES;
for (i = 0; i < 12; i++) {
//check other results to fill in ?
if (previousNumberArray[0][i] != NOT_FOUND ) {
//Number are equal increase sureness until limit
if (previousNumberArray[0][i] == aNumberArray[0][i]) {
if (previousNumberArray[2][i] != SURENESS_LIMIT)
++previousNumberArray[2][i];
}
else {
if (aNumberArray[0][i] == NOT_FOUND) {
// A aNumberArray is never used so no need to fill it
//aNumberArray[0][i] = previousNumberArray[0][i];
//aNumberArray[1][i] = previousNumberArray[1][i];
}
else {
// decide on the sureness index which one stays
// if the previous number sureness ahs dipped under 0 then replace it with the current number
// else if the current number sureness is higher than previous number then replace previous number as well
// subtract 1 from sureness as the numbers where not matching
if (previousNumberArray[2][i] < 0) {
previousNumberArray[0][i] = aNumberArray[0][i];
previousNumberArray[1][i] = aNumberArray[1][i];
previousNumberArray[2][i] = 0;
}
else {
if (previousNumberArray[2][i] < aNumberArray[0][i]) {
previousNumberArray[0][i] = aNumberArray[0][i];
//previousNumberArray[1][i] = aNumberArray[1][i]; // We leave this line out as we don't want to carry the surenees from a single bad scan
}
--previousNumberArray[2][i];
}
}
}
}
else {
if (aNumberArray[0][i] == NOT_FOUND ) {
completeNumber = NO;
}
else {
previousNumberArray[0][i] = aNumberArray[0][i];
previousNumberArray[1][i] = aNumberArray[1][i];
previousNumberArray[2][i] = 0;
}
}
}
return completeNumber;
}
#pragma mark -
#pragma mark Frequency
- (void)addNumber:(char [3][12])aNumberArray toFrequencyMatrix:(char[10][13])aFrequencyMatrix {
int i;
int firstDigit = aNumberArray[1][6];
if (firstDigit != NOT_FOUND)
aFrequencyMatrix[firstDigit][0]++;
for (i = 0; i < 12; i++) {
int nextDigit = aNumberArray[0][i];
if (nextDigit != NOT_FOUND)
aFrequencyMatrix[nextDigit][i+1]++;
}
}
- (void)addParity:(char [3][12])aNumberArray toParityMatrix:(char[2][6])aParityMatrix {
int i;
for (i = 0; i < 6; i++) {
int nextDigit = aNumberArray[1][i];
if (nextDigit == 0)
aParityMatrix[0][i]++;
else if (nextDigit == 1)
aParityMatrix[1][i]++;
}
}
- (void)addFrequencyMatrix:(char[10][13])aFrequencyMatrix toFrequencyMatrix:(char[10][13])anotherFrequencyMatrix {
int i, j;
for (i = 0; i < 10; i++) {
for (j = 0; j < 13; j++) {
anotherFrequencyMatrix[i][j] += aFrequencyMatrix[i][j];
}
}
}
- (int)parityFromFrequency:(char[2][6])aParityMatrix {
int j;
char parityArray[7] = {0};
parityArray[6] = NOT_FOUND;
for (j = 1; j < 6; j++) { //First number is always odd, so don't use it
if (aParityMatrix[0][j] > aParityMatrix[1][j])
parityArray[j] = 0;
else if (aParityMatrix[1][j] != 0) //si hay valor entonces odd
parityArray[j] = 1;
else
parityArray[j] = NOT_FOUND;
}
//NSLog(@"Parity digit: %d", parityArray[6]);
[self calculateFirstNumberFromOddEvenParity:parityArray];
return parityArray[6];
}
- (NSString *)numberFromFrequencyMatrix:(char [10][13])aFrequencyMatrix {
NSMutableString *numberString = [[NSMutableString alloc] init];
int i, j;
for (j = 0; j < 13; j++) {
int highestNumber = NOT_FOUND, highestCount = 0;
for (i = 0; i < 10; i++) {
if (aFrequencyMatrix[i][j] != 0 && aFrequencyMatrix[i][j] > highestCount) {
highestCount = aFrequencyMatrix[i][j];
highestNumber = i;
}
}
if (highestNumber == NOT_FOUND)
[numberString appendString:@"?"];
else
[numberString appendString:[NSString stringWithFormat:@"%d", highestNumber]];
}
//NSLog(@"%@", numberString);
return [numberString autorelease];
}
- (BOOL)numberFromFrequencyMatrix:(char [10][13])aFrequencyMatrix toArray:(char [13])anEANArray {
int i, j;
for (j = 0; j < 13; j++) {
int highestNumber = NOT_FOUND, highestCount = 0;
for (i = 0; i < 10; i++) {
if (aFrequencyMatrix[i][j] != 0 && aFrequencyMatrix[i][j] > highestCount) {
highestCount = aFrequencyMatrix[i][j];
highestNumber = i;
}
}
if (highestNumber == NOT_FOUND) {
//anEANArray[j] = NOT_FOUND;
return NO;
}
else
anEANArray[j] = highestNumber;
}
return YES;
}
#pragma mark -
- (BOOL)isValidCheckDigit:(char [13])anEANArray {
int i, checkDigitSum = 0;
for (i = 0; i < 12; i++) {
if ( i % 2 == 0)
checkDigitSum = checkDigitSum + anEANArray[i];
else
checkDigitSum = checkDigitSum + (anEANArray[i] * 3);
}
//NSLog(@"check Sum %d", checkDigitSum);
checkDigitSum = 10 - (checkDigitSum % 10);
checkDigitSum = checkDigitSum % 10;
//NSLog(@"check %d == %d", checkDigitSum, anEANArray[12]);
if (checkDigitSum == anEANArray[12])
return YES;
return NO;
}
- (NSString *)barcodeFromEANArray:(char [13])anEANArray {
NSMutableString *numberString = [NSMutableString string];
int i;
for (i = 0; i < 13; i++) {
if (anEANArray[i] == NOT_FOUND)
[numberString appendString:@"?"];
else
[numberString appendString:[NSString stringWithFormat:@"%d", anEANArray[i]]];
}
return numberString;
}
// Turns a C array of a number into a string
// Sections can be uncommented to print more info about the number
- (NSString *)barcodeFromArray:(char [3][12])aNumberArray {
NSMutableString *numberString = [NSMutableString string];
//NSMutableString *sureness = [NSMutableString string];
//NSMutableString *evenODD = [NSMutableString string];
//It's a UPC don't include the leading zero from the EAN
if (aNumberArray[1][6] != 0)
[numberString appendFormat:@"%d", aNumberArray[1][6]];
int i;
for (i = 0; i < 12; i++) {
int numberValue = aNumberArray[0][i];
if (numberValue == NOT_FOUND)
[numberString appendString:@"?"];
else
[numberString appendString:[NSString stringWithFormat:@"%d", numberValue]];
//[sureness appendString:[NSString stringWithFormat:@"%d ", aNumberArray[2][i]]];
//[evenODD appendString:[NSString stringWithFormat:@"%d", aNumberArray[1][i]]];
}
//NSLog(@"Number : %@", numberString);
//NSLog(@"Surenes: %@ ", sureness);
//NSLog(@"evenOdd: %@ ", evenODD);
return numberString;
}
- (void)clearGlobalFrequency {
int i, j;
for (i = 0; i < 10; i++) {
for (j = 0; j < 13; j++) {
globalFrequencyMatrix[i][j] = 0;
}
}
frameCount = 0;
//[self clearNumberArray:previousNumberGlobalArray];
}
/*
- (void)clear:(id)sender {
int i;
for (i = 0; i < 12; i++) {
previousNumberGlobalArray[0][i] = NOT_FOUND;
previousNumberGlobalArray[2][i] = 0;
}
previousNumberGlobalArray[1][6] = NOT_FOUND;
//NSLog(@"Clear: %@", [self barcodeFromArray:previousNumberLocalArray]);
}
*/
- (void)clearNumberArray:(char [3][12])aNumberArray {
int i;
for (i = 0; i < 12; i++) {
aNumberArray[0][i] = NOT_FOUND;
aNumberArray[1][i] = NOT_FOUND;
aNumberArray[2][i] = 0;
}
}
#pragma mark -
#pragma mark Debug only
#ifdef DEBUG
- (NSString *)stringFromBars:(int [62])aNumberArray {
NSMutableString *numberString = [NSMutableString string];
int i;
for (i = 0; i < 62; i++) {
[numberString appendString:[NSString stringWithFormat:@"%d ", aNumberArray[i]]];
}
return numberString;
}
- (BOOL)checkCheckDigit:(char [3][12])aNumberArray {
//NSLog(@"numberToCheck %@", [NSString stringWithFormat:@"%d%@", aNumberArray[1][6], [self barcodeFromArray:aNumberArray]]);
//check digit
// the first digit is in [1][6]
// 11 total count becasue we want to leave the check digit out of the calculation
int i, checkDigitSum = aNumberArray[1][6];
for (i = 0; i < 11; i++) {
if ( i % 2)
checkDigitSum = checkDigitSum + aNumberArray[0][i];
else
checkDigitSum = checkDigitSum + (aNumberArray[0][i] * 3);
}
//NSLog(@"check Sum %d", checkDigitSum);
checkDigitSum = 10 - (checkDigitSum % 10);
//NSLog(@"check %d == %d", checkDigitSum, aNumberArray[0][11]);
//OLD WAY if (checkDigitSum == aNumberArray[0][11] || (checkDigitSum == 10 && aNumberArray[0][11] == 0))
checkDigitSum = checkDigitSum % 10;
if (checkDigitSum == aNumberArray[0][11])
return YES;
return NO;
}
- (void)printFrequencyMatrix:(char [10][13])aFrequencyMatrix {
NSMutableString *numberString = [NSMutableString string];
[numberString appendString:@"\n"];
int i, j;
for (i = 0; i < 10; i++) {
for (j = 0; j < 13; j++) {
[numberString appendString:[NSString stringWithFormat:@"%d\t", aFrequencyMatrix[i][j]]];
}
[numberString appendString:@"\n"];
}
MyLog(@"%@", numberString);
}
- (void)printParityMatrix:(char[2][6])aParityMatrix {
NSMutableString *numberString = [NSMutableString string];
[numberString appendString:@"Parity\n"];
int i, j;
for (i = 0; i < 2; i++) {
for (j = 0; j < 6; j++) {
[numberString appendString:[NSString stringWithFormat:@"%d\t", aParityMatrix[i][j]]];
}
[numberString appendString:@"\n"];
}
MyLog(@"%@", numberString);
}
#endif
#ifdef DEBUG
#pragma mark -
#pragma mark Experimental
// Determine the area of the barcode by using the supplied center and determining the average change of direction
// As soon as something is below 1/4 of the variation in height for more than the average spacing time MULTIPLY_AVERAGE_SPACING_OLD, then the barcode ends
- (void)findStartOld:(int *)startBarcode end:(int *)endBarcode forLine:(int *)pixelLineArray derivative:(int *)lineDerivativeArray centerAt:(int)centerX min:(int *)minValue max:(int *)maxValue {
int averageSpacing = 0, numberOfCurves = 0, spacingInThisRun = 0;
int i, count, startScan = centerX - 40, endScan = centerX + 40;
BOOL positive = YES;
if (lineDerivativeArray[startScan] < 0)
positive = NO;
//Build the average spacing number and the variation in height from a smaple around the center
for (i = startScan; i < endScan; i++) {
if (*maxValue < pixelLineArray[i])
*maxValue = pixelLineArray[i];
else if (*minValue > pixelLineArray[i])
*minValue = pixelLineArray[i];
if (lineDerivativeArray[i] < 0) {
if (positive) {
positive = NO;
averageSpacing = averageSpacing + spacingInThisRun;
numberOfCurves++;
spacingInThisRun = 0;
}
}
else {
if (!positive) {
positive = YES;
averageSpacing = averageSpacing + spacingInThisRun;
numberOfCurves++;
spacingInThisRun = 0;
}
}
spacingInThisRun++;
}
//If there was a solid growing gradient with no curves then return
if (numberOfCurves == 0)
return;
averageSpacing = (averageSpacing / numberOfCurves) * MULTIPLY_AVERAGE_SPACING_OLD;
//NSLog(@"min %d max %d spacing: %d", *minValue, *maxValue, averageSpacing);
int quarterHeight = ((*maxValue - *minValue) * 0.25);
int bottomForth = *minValue + quarterHeight;
int topForth = *maxValue - quarterHeight;
//anything below oneForth for averageSpacing * MULTIPLY_AVERAGE_SPACING_OLD is the end of the barcode
for (i = centerX, count = 0; i > 0; i--) {
if (pixelLineArray[i] < bottomForth || pixelLineArray[i] > topForth) {
count++;
}
else {
count = 0;
*startBarcode = i;
}
if (count > averageSpacing) {
break;
}
}
for (i = centerX, count = 0; i < width; i++) {
if (pixelLineArray[i] < bottomForth || pixelLineArray[i] > topForth) {
count++;
}
else {
count = 0;
*endBarcode = i;
}
if (count > averageSpacing) {
break;
}
}
//NSLog(@"beginning: %d end %d", *startBarcode, *endBarcode);
}
- (void)getPeaksForLine:(int *)pixelLineArray start:(int)startBarcode end:(int)endBarcode peaks:(int [70][2])peakValleyInformation {
int zeroDerivativeCloseToZero = 0;
//BOOL lookForSlopes = NO;
//int lookingForSlopeSign;
int lowestPeak = 255, heighestPeak = 0, heighestValley = 0, lowestValley = 255;
int barcodeThickness = endBarcode - startBarcode;
float averageBarWidth = barcodeThickness / 95.0;
//int peakValleyInformation[70][2] = {0}; //First row peak index, second height
int mn = 256, mx = -256;
//int mnpos, mxpos;
int delta = 0;
//int delta = barcodeThickness / 100; //Should be dependant on the width of the barcode
int lookformax = YES;
int index = startBarcode;
int peakNumber = 0;
int count = 0;
//int i, j;
NSBezierPath *aPath = [NSBezierPath bezierPath];
NSBezierPath *valleyPath = [NSBezierPath bezierPath];
//NSBezierPath *blackPath = [NSBezierPath bezierPath];
/*
int newLine[640] = {0};
[self binarizeLine:pixelLineArray to:newLine start:startBarcode width:barcodeThickness];
NSBezierPath *binaryPath = [NSBezierPath bezierPath];
for (j = 0; j < 640 ; j++) {
if (newLine[j] == BLACK_PIXEL) {
[binaryPath moveToPoint:NSMakePoint(j, 0)];
[binaryPath lineToPoint:NSMakePoint(j, 255)];
}
}
[barcodeView setPath:binaryPath withColor:[NSColor blackColor]];
return;
*/
while (index < endBarcode && peakNumber < 69) {
int current = pixelLineArray[index];
if (current > mx) {
mx = current;
//mxpos = index;
}
if (current < mn) {
mn = current;
//mnpos = index;
}
// Looking for small slopes that are small peaks
int secondDerivative = pixelLineArray[index + 1] - current;
if (lookformax) {
if (secondDerivative < 4) {
zeroDerivativeCloseToZero = index;
}
else {
if (zeroDerivativeCloseToZero != 0) {
// Only add if the lest a bar width away from the previous peak,
//!!! should check against the upcoming peak as well
if (index - peakValleyInformation[peakNumber -1][0] > averageBarWidth) {
//add peak and valley
peakValleyInformation[peakNumber][0] = zeroDerivativeCloseToZero;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
peakValleyInformation[peakNumber][0] = index;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
[aPath moveToPoint:NSMakePoint(zeroDerivativeCloseToZero, 0)];
[aPath lineToPoint:NSMakePoint(zeroDerivativeCloseToZero, 350)];
}
zeroDerivativeCloseToZero = 0;
}
}
//peak
if (current < (mx - delta)) {
if (current < lowestPeak)
lowestPeak = current;
if (current > heighestPeak)
heighestPeak = current;
[aPath moveToPoint:NSMakePoint(index -1, 255)];
[aPath lineToPoint:NSMakePoint(index -1, 0)];
peakValleyInformation[peakNumber][0] = index -1;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
mn = current;
//mnpos = index;
lookformax = NO;
count = -1;
zeroDerivativeCloseToZero = 0;
}
}
else {
if (secondDerivative > -4) {
zeroDerivativeCloseToZero = index;
}
else {
if (zeroDerivativeCloseToZero != 0) {
// Only add if the lest a bar width away from the previous peak,
if (index - peakValleyInformation[peakNumber - 1][0] > averageBarWidth) {
//add peak and valley
peakValleyInformation[peakNumber][0] = zeroDerivativeCloseToZero;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
peakValleyInformation[peakNumber][0] = index;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
[aPath moveToPoint:NSMakePoint(zeroDerivativeCloseToZero, 0)];
[aPath lineToPoint:NSMakePoint(zeroDerivativeCloseToZero, 350)];
}
zeroDerivativeCloseToZero = 0;
}
}
//valley
if (current > (mn + delta)) {
if (current > heighestValley)
heighestValley = current;
if (current < lowestValley)
lowestValley = current;
[valleyPath moveToPoint:NSMakePoint(index -1, 255)];
[valleyPath lineToPoint:NSMakePoint(index -1, 0)];
peakValleyInformation[peakNumber][0] = index -1;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
mx = current;
//mxpos = index;
lookformax = YES;
zeroDerivativeCloseToZero = 0;
}
}
index++;
count++;
}
}
/* This is a route is in development.
Locating only the peaks of the barcode and infering the barcode from the peaks.
Although the peaks contain less information they are not affected by edge blurring
So using the space between peaks and statistical analysis could lead to the barcode
Normalized distance between peaks should reflect the following:
1 would mean it's 1 black stripe and 1 white stripe
1.5 would mean it's 1 black and 2 white or 2 black and 1 white;
2 = 1-3, 2-2, 3-1
2.5 = 1-4, 2-3, 3-2, 1-4
Using the next sorounding distances it can tell which possability it should be.
*/
//Experimentation with finding out the distance between peaks as they are not as sucetible to blurring
- (void)getPeakDistanceForLine:(int *)pixelLineArray start:(int)startBarcode end:(int)endBarcode averageHeight:(int)averageHeight barArray:(int *)barsArray {
/*
//Test average scanner
float testBarWidth[70] = {0};
testBarWidth[0] = 1;
testBarWidth[1] = 1;
testBarWidth[2] = 1;
testBarWidth[3] = 1;
testBarWidth[4] = 3;
testBarWidth[5] = 1;
testBarWidth[6] = 2;
testBarWidth[7] = 3;
testBarWidth[8] = 1;
testBarWidth[9] = 2;
testBarWidth[10] = 1;
testBarWidth[11] = 1;
[self decodeNumberBasedOnCloseMatch:testBarWidth];
*/
int zeroDerivativeCloseToZero = 0;
//BOOL lookForSlopes = NO;
//int lookingForSlopeSign;
int lowestPeak = 255, heighestPeak = 0, heighestValley = 0, lowestValley = 255;
int barcodeThickness = endBarcode - startBarcode;
float averageBarWidth = barcodeThickness / 95.0;
int peakValleyInformation[70][2] = {0}; //First row peak index, second height
int mn = 256, mx = -256;
//int mnpos, mxpos;
int delta = 0;
//int delta = barcodeThickness / 100; //Should be dependant on the width of the barcode
int lookformax = YES;
int index = startBarcode;
int peakNumber = 0;
int count = 0;
int i, j;
NSBezierPath *aPath = [NSBezierPath bezierPath];
NSBezierPath *valleyPath = [NSBezierPath bezierPath];
NSBezierPath *blackPath; // = [NSBezierPath bezierPath];
/*
int newLine[640] = {0};
[self binarizeLine:pixelLineArray to:newLine start:startBarcode width:barcodeThickness];
NSBezierPath *binaryPath = [NSBezierPath bezierPath];
for (j = 0; j < 640 ; j++) {
if (newLine[j] == BLACK_PIXEL) {
[binaryPath moveToPoint:NSMakePoint(j, 0)];
[binaryPath lineToPoint:NSMakePoint(j, 255)];
}
}
[barcodeView setPath:binaryPath withColor:[NSColor blackColor]];
return;
*/
while (index < endBarcode && peakNumber < 69) {
int current = pixelLineArray[index];
if (current > mx) {
mx = current;
//mxpos = index;
}
if (current < mn) {
mn = current;
//mnpos = index;
}
// Looking for small slopes that are small peaks
int secondDerivative = pixelLineArray[index + 1] - current;
if (lookformax) {
if (secondDerivative < 4) {
zeroDerivativeCloseToZero = index;
}
else {
if (zeroDerivativeCloseToZero != 0) {
// Only add if the least a bar width away from the previous peak,
//!!! should check against the upcoming peak as well
if (index - peakValleyInformation[peakNumber -1][0] > averageBarWidth) {
//add peak and valley
peakValleyInformation[peakNumber][0] = zeroDerivativeCloseToZero;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
peakValleyInformation[peakNumber][0] = index;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
[aPath moveToPoint:NSMakePoint(zeroDerivativeCloseToZero, 0)];
[aPath lineToPoint:NSMakePoint(zeroDerivativeCloseToZero, 350)];
}
zeroDerivativeCloseToZero = 0;
}
}
//peak
if (current < (mx - delta)) {
if (current < lowestPeak)
lowestPeak = current;
if (current > heighestPeak)
heighestPeak = current;
[aPath moveToPoint:NSMakePoint(index -1, 255)];
[aPath lineToPoint:NSMakePoint(index -1, 0)];
peakValleyInformation[peakNumber][0] = index -1;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
mn = current;
//mnpos = index;
lookformax = NO;
count = -1;
zeroDerivativeCloseToZero = 0;
}
}
else {
if (secondDerivative > -4) {
zeroDerivativeCloseToZero = index;
}
else {
if (zeroDerivativeCloseToZero != 0) {
// Only add if the lest a bar width away from the previous peak,
if (index - peakValleyInformation[peakNumber - 1][0] > averageBarWidth) {
//add peak and valley
peakValleyInformation[peakNumber][0] = zeroDerivativeCloseToZero;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
peakValleyInformation[peakNumber][0] = index;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
[aPath moveToPoint:NSMakePoint(zeroDerivativeCloseToZero, 0)];
[aPath lineToPoint:NSMakePoint(zeroDerivativeCloseToZero, 350)];
}
zeroDerivativeCloseToZero = 0;
}
}
//valley
if (current > (mn + delta)) {
if (current > heighestValley)
heighestValley = current;
if (current < lowestValley)
lowestValley = current;
[valleyPath moveToPoint:NSMakePoint(index -1, 255)];
[valleyPath lineToPoint:NSMakePoint(index -1, 0)];
peakValleyInformation[peakNumber][0] = index -1;
peakValleyInformation[peakNumber][1] = current;
peakNumber++;
mx = current;
//mxpos = index;
lookformax = YES;
zeroDerivativeCloseToZero = 0;
}
}
index++;
count++;
}
//Add the last bar as it's not added above
//peakValleyInformation[peakNumber-1][0] = peakValleyInformation[peakNumber-1][0] + averageBarWidth;
//peakValleyInformation[peakNumber][1] = peakValleyInformation[peakNumber][0];
#ifdef DEBUG
[graphView setPath:aPath withColor:[NSColor redColor]];
[graphView setPath:valleyPath withColor:[NSColor darkGrayColor]];
//NSLog(@"peak: high %d low: %d", heighestPeak, lowestPeak);
//NSLog(@"valley: high %d low: %d", heighestValley, lowestValley);
#endif DEBUG
//NSBezierPath *curvePath = [NSBezierPath bezierPath];
//There should be 58 peaks and valleys 30 black peaks and 28 valleys in between
if (peakNumber > 30 && peakNumber < 70) {
int k;
int largestDifference = (heighestPeak - lowestValley);
float averageHeightOfCode = largestDifference * 0.5 + lowestValley;
/*
//float widthBarcode = (peakValleyInformation[peakNumber - 1][0] - peakValleyInformation[0][0]);
//float oneBarWidth = widthBarcode / 95.0;
int twentyPercentHeight = largestDifference * 0.2;
int tenPercentHeight = largestDifference * 0.1;
//int fourPercentHeight = largestDifference * 0.04;
//NSLog(@"diff: %d twenty: %d", largestDifference, twentyPercentHeight);
int highRangeForOne = lowestPeak + twentyPercentHeight;
int lowRangeForOne = heighestValley - twentyPercentHeight;
//int highRangeForOne = averageHeightOfCode + twentyPercentHeight;
//int lowRangeForOne = averageHeightOfCode - twentyPercentHeight;
//int barsArray[70] = {0}; //Should be 58 bars, but 70 to get some over scans
//Make the first peak, valley, peak all one
barsArray[0] = 1;
//barsArray[1] = 1;
//barsArray[2] = 1;
//[curvePath moveToPoint:NSMakePoint(peakValleyInformation[0][0] + ((peakValleyInformation[1][0] - peakValleyInformation[0][0]) / 2), peakValleyInformation[0][1] - ((peakValleyInformation[1][1] - peakValleyInformation[0][1]) / 2))];
//int k = 0;
//heighestValley = heighestValley * 1.1;
//lowestPeak = lowestPeak * 0.9;
//start at valley 2
for (i = 1; i < peakNumber - 1 ; i++) {
//int x = peakValleyInformation[i][0] + (peakValleyInformation[i+1][0] - peakValleyInformation[i][0]) / 2;
//int y = peakValleyInformation[i][1] - (peakValleyInformation[i+1][1] - peakValleyInformation[i][1]) / 2;
//[curvePath lineToPoint:NSMakePoint(x, y)];
int heightOfPoint = peakValleyInformation[i][1];
//int widthOfPoint = peakValleyInformation[i+1][0] - peakValleyInformation[i-1][0];
// int previousPointHeight = peakValleyInformation[i-1][1];
// int nextPointHeight = peakValleyInformation[i+1][1];
// int previousWidth = peakValleyInformation[i][0] - peakValleyInformation[i-2][0];
// int nextWidth = peakValleyInformation[i+2][0] - peakValleyInformation[i][0];
//based on height of peak
if (i % 2 == 0) { //peak
if (heightOfPoint < highRangeForOne)
barsArray[i] = 1;
else if (heightOfPoint > heighestPeak - tenPercentHeight)
barsArray[i] = 3; //or 4
else
barsArray[i] = 2;
}
else { // valley
if (heightOfPoint > lowRangeForOne)
barsArray[i] = 1;
else if (heightOfPoint < lowestValley + tenPercentHeight)
barsArray[i] = 3; //or 4
else
barsArray[i] = 2;
}
}
barsArray[peakNumber - 1] = 1; // The last peak
//return;
//[graphView setPath:curvePath withColor:[NSColor purpleColor]];
*/
int blackAndWhite[640] = {0};
//There should be 58 peaks and valleys 30 black peaks and 28 valleys in between
if (peakNumber > 30 && peakNumber < 70) {
float widthBarcode = (peakValleyInformation[peakNumber - 1][0] - peakValleyInformation[0][0]);
averageBarWidth = widthBarcode / 95.0;
for (i = 0; i < peakNumber - 1 ; i++) {
int locationOfBar = peakValleyInformation[i][0];
int locationLastBar = peakValleyInformation[i+1][0];
int heightOfBar = peakValleyInformation[i][1];
int heightLastBar = peakValleyInformation[i+1][1];
float averageHeightOfSection = (heightOfBar + heightLastBar) / 2.0;
float ratioToAdjust = averageHeightOfSection / averageHeightOfCode;
if (ratioToAdjust > 1) {
ratioToAdjust = ratioToAdjust - 1;
ratioToAdjust = 1 - ratioToAdjust;
}
//else
//ratioToAdjust = 1 - ratioToAdjust;
if (heightOfBar > heightLastBar) {
averageHeightOfSection = heightLastBar + ((heightOfBar - heightLastBar) * ratioToAdjust) / 2;
}
else
averageHeightOfSection = heightOfBar + ((heightLastBar - heightOfBar) * ratioToAdjust) / 2;
for (j = locationOfBar; j < locationLastBar; j++) {
//NSLog(@"%d\t\t%f", peakValleyInformation[j][1], averageHeightOfSection);
if (pixelLineArray[j] > averageHeightOfSection) {
//NSLog(@"%d black", j);
//[blackPath moveToPoint:NSMakePoint(j, 0)];
//[blackPath lineToPoint:NSMakePoint(j, 255)];
blackAndWhite[j] = BLACK_PIXEL;
}
//else
//NSLog(@"%d white", j);
}
}
}
//[barcodeView setPath:blackPath withColor:[NSColor blackColor]];
//Count thickness
int thickness[70] = {0};
int lastPeak = peakValleyInformation[peakNumber - 1][0], firstPeak = peakValleyInformation[0][0];
BOOL inBlack = YES;
int count = 0, largestCount = 0, smallestCount = 255;
for (k = 0, i = firstPeak; i < lastPeak; i++) {
int nextColor = blackAndWhite[i];
if ((inBlack && nextColor == WHITE_PIXEL) || (!inBlack && nextColor == BLACK_PIXEL)) {
thickness[k] = count;
k++;
inBlack = !inBlack;
if (count > largestCount)
largestCount = count;
if (count < smallestCount)
smallestCount = count;
count = 0;
}
count++;
}
//fill out the other bars counts based on the thickness
//NSLog(@"small: %d large: %d", smallestCount, largestCount);
//NSLog(@"%f", averageBarWidth);
//int averageThicknessBasedOnFattest = largestCount / 3.0;
for (i = 0; i < peakNumber - 1 ; i++) {
if (barsArray[i] == 0) {
barsArray[i] = [self getNumberfromThickness:thickness[i] oneBar:averageBarWidth];
}
}
// Draw bars in 1200 pixel for debugging
blackPath = [NSBezierPath bezierPath];
int movingLocation = 0;
for (i = 0; i < 62; i++) {
int displayWidth = barsArray[i] * 4;
//black
if (i % 2 == 0) {
for (j = 0; j < displayWidth; j++) {
[blackPath moveToPoint:NSMakePoint(movingLocation, 255)];
[blackPath lineToPoint:NSMakePoint(movingLocation, 0)];
movingLocation++;
}
}
else
movingLocation += displayWidth; //white
}
/*
//draw one width
int movingLocation = 1250;
int displayWidth = averageBarWidth / widthBarcode * 1200;
for (j = 0; j < displayWidth; j++) {
[blackPath moveToPoint:NSMakePoint(movingLocation, 255)];
[blackPath lineToPoint:NSMakePoint(movingLocation, 0)];
movingLocation++;
}
*/
#ifdef DEBUG
[barcodeView setPath:blackPath withColor:[NSColor blackColor]];
[barcodeView display];
[barcodeView removeAllPaths];
#endif
//print bars
NSMutableString *numberString = [NSMutableString string];
for (i = 0; i < 62; i++) {
[numberString appendString:[NSString stringWithFormat:@"%d ", barsArray[i]]];
}
//NSLog(@"%@", numberString);
[self clearNumberArray:numberArray];
[self readBarsStartingWithHeaderBars:barsArray];
//NSLog(@"Scanned %@ ", [self barcodeFromArray:numberArray]);
//NSString *barcodeNumberAvergae = [self decodeNumberBasedOnCloseMatch:barsArray];
//NSLog(@"based on best guess: %@", barcodeNumberAvergae);
}
}
//given a array of bar thickness it read four bars at a time and determines the number based on the encoding
- (void)readBarsStartingWithHeaderBars:(int [62])lastBinaryData {
int k, j;
if (lastBinaryData[0] == 1 && lastBinaryData[1] == 1 && lastBinaryData[2] == 1) {
int i = 3;
k = 0;
//First number has to be odd encoded
[self getNumberForLocation:&lastBinaryData[i] encoding:MKLeftHandEncodingOdd location:k];
if (numberArray[0][k] == NOT_FOUND) {
//turn any 3 bars into 4 bars and try again
for (j = i; j < i + 4; j++)
if (lastBinaryData[j] == 3)
lastBinaryData[j] = 4;
[self getNumberForLocation:&lastBinaryData[i] encoding:MKRightHandEncoding location:k];
}
if (numberArray[0][k] != NOT_FOUND)
numberOfDigitsFound++;
//First Section left hand encoding even or odd
for (i = i +4, k = 1; i < 28; i = i + 4, k++) {
[self getNumberForLocation:&lastBinaryData[i] encoding:MKLeftHandEncodingBoth location:k];
if (numberArray[0][k] == NOT_FOUND) {
//turn any 3 bars into 4 bars and try again
for (j = i; j < i + 4; j++)
if (lastBinaryData[j] == 3)
lastBinaryData[j] = 4;
[self getNumberForLocation:&lastBinaryData[i] encoding:MKLeftHandEncodingBoth location:k];
}
if (numberArray[0][k] != NOT_FOUND)
numberOfDigitsFound++;
}
//Second section all right hand encoding
for (i = i+5; i < 57; i = i + 4, k++) {
[self getNumberForLocation:&lastBinaryData[i] encoding:MKRightHandEncoding location:k];
if (numberArray[0][k] == NOT_FOUND) {
//turn any 3 bars into 4 bars and try again
for (j = i; j < i + 4; j++)
if (lastBinaryData[j] == 3)
lastBinaryData[j] = 4;
[self getNumberForLocation:&lastBinaryData[i] encoding:MKRightHandEncoding location:k];
}
if (numberArray[0][k] != NOT_FOUND)
numberOfDigitsFound++;
}
}
}
- (int)getNumberfromThickness:(int)thickness oneBar:(float)aBarWidth {
double ratioToAverageThickness = thickness / aBarWidth;
//NSLog(@"%d %f", thickness, aBarWidth);
//NSLog(@"%f", ratioToAverageThickness);
if (ratioToAverageThickness < 1.5)
return 1;
else if (ratioToAverageThickness < 2.7)
return 2;
else
return 3;
}
#endif
@end