• R/O
  • HTTP
  • SSH
  • HTTPS

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

iSightを使ってBooklog,MediaMarkerインポート用CSVファイルを生成するアプリ


File Info

Rev. deec2c46d1e4b89d37ea473242737695cab87712
Tamanho 72,759 bytes
Hora 2012-03-25 16:55:28
Autor masakih
Mensagem de Log

[Mod] PPCサポートをやめた

Content

//
//  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