In these examples 1D barcodes (Code39 and Code128) are read. To see how to read 2D and postal barcode click here.
ClearImage readers utilize multiple internal image enhancement and optimization techniques to read substandard barcodes from poor quality images. Many image impairments can be overcome by these optimizations. However, there will always be barcodes whose peculiar impairments and quality issues are so severe or bizarre that they cannot be read with the standard engines. We refer to them as “corner case” barcodes.
The preferred method to address such corner cases is use of Inlite’s Targeted Barcode Reading technology.. This page describes an alternative approach to repair barcodes before recognition through use of image processing engines. Improving barcode and scanned image quality is also discussed.
The examples below show how to use the ImageEditor object (.NET API) or the CiRepair and CiTools objects (COM API) to improve such corner case images before invoking the barcode reader engine. The specific choice of the AdvancedBinarize method in these examples is purely for code demonstration and most probably will NOT help to read your barcodes. To obtain expert help with images that are not read by your application or the demo application (from the ClearImage SDK) contact Inlite to solve your barcode recognition challenge.
Read poor quality barcodes from an image page
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using Inlite.ClearImageNet; // . . . void ReadBarcode1DpreProcfromPage(string fileName, int page) { ImageEditor editor = new ImageEditor(); try { // Preprocess page editor.Image.Open(fileName, page); editor.AdvancedBinarize(); // Read barcodes BarcodeReader reader = new BarcodeReader(); reader.Code39 = true; reader.Code128 = true; Barcode[] barcodes = reader.Read(editor); foreach (Barcode bc in barcodes) ProcessBarcode(bc); } catch (Exception ex) { ProcessException(ex); } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Private Sub ReadBarcode1DpreProcfromPage(fileName As String, page As Integer) Dim editor As New ImageEditor() Try ' Preprocess page editor.Image.Open(fileName, page) editor.AdvancedBinarize() ' Read barcodes Dim reader As New BarcodeReader() reader.Code39 = True reader.Code128 = True Dim barcodes As Barcode() = reader.Read(editor) For Each bc As Barcode In barcodes ProcessBarcode(bc) Next Catch ex As Exception ProcessException(ex) End Try End Sub |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void ReadBarcode1DPreProcess (const char *fileName, const long page) { try { // Preprocess image ICiRepairPtr editor; editor = Ci->CreateRepair (); editor->Image->Open (_bstr_t(fileName), page); editor->AdvancedBinarize(0, 0, 0); // Read barcodes ICiBarcodeProPtr reader; reader = Ci->CreateBarcodePro (); reader->Type = (FBarcodeType) (cibfCode39 | cibfCode128); reader->Image = editor->Image; reader->Find(0); for (int i = 1 ; i <= reader->Barcodes->Count ; i++) ProcessBarcode (reader->Barcodes->Item[i]); } catch (_com_error &ex) {ProcessException(ex);} } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
void ReadBarcode1DPreProc(String fileName, int page) { try { // Create pre-processing ICiRepair editor = Ci.CreateRepair(); // Create and configure barcode reader ICiBarcodePro reader = Ci.CreateBarcodePro(); reader.setType(new FBarcodeType(FBarcodeType.cibfCode39, FBarcodeType.cibfCode128)); // Multiple types // Pre-process image editor.getImage().Open(fileName, page); editor.AdvancedBinarize(0, 0, 0); // Read barcodes reader.setImage(editor.getImage()); reader.Find(0); // Process results for (int i = 1; i <= reader.getBarcodes().getCount(); i++) { ICiBarcode Bc = reader.getBarcodes().getItem(i); System.out.println(Bc.getText()); // Use text value OR ProcessBarcode(Bc); // do other processing } } catch (Exception ex) { ProcessException(ex); } finally { // Collect garbage periodically to release JNI-managed objects System.gc(); } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function ReadBarcode1DPreProc ($fileName, $page) { // Create ClearImage COM Server $Ci = new COM("ClearImage.ClearImage"); // Creat-preprocessor $editor = $Ci->CreateRepair(); // Creare and configure barcode reader $reader = $Ci->CreateBarcodePro(); $cibfCode39 = 2; $cibfCode128 = 4; $reader->Type = $cibfCode39 + $cibfCode128; // Pre-process image $editor->Image->Open($fileName, $page); $editor->AdvancedBinarize (0,0,0); // Read barcodes $reader->Image = $editor->Image; $BCcount = $reader->Find(0); // Process Results for ($i=1;$i<=$BCcount;$i++) { $Bc = $reader->BarCodes($i); echo "$Bc->Text\n\r"; } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
uses ...,ClearImage_TLB, comobj; // . . . procedure ReadBarcode1DPreProc(const fileName: string; const page: integer); var Ci: ICiServer; editor: ICiRepair; reader: ICiBarcodePro; i: integer; begin try try begin //Create and configure reader Ci:=CoCiServer.Create; editor:=Ci.CreateRepair; reader:=Ci.CreateBarcodePro; reader.Type_:=cibCode39 or cibCode128; // Preprocess Image editor.Image.Open(fileName, page); editor.AdvancedBinarize(0, 0, 0); // Read barcodes reader.Image = editor.Image reader.Find(0); // Process results for i := 1 to reader.Barcodes.Count do begin ShowMessage(reader.Barcodes.Item[i].Text) ; end; end except on E:Exception do //Process errors ShowMessage(Format('Error:%s.File:%s', [E.Message,FileName])); end finally end end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Sub ReadBarcode1DPrepProc(fileName, page) On Error Resume Next 'Create CiServer object: Set Ci = CreateObject("ClearImage.ClearImage") ' Create pre-processor Set editor = Ci.CreateRepair If Err.Number <> 0 Then WScript.Echo Err.Description : Exit Sub ' Create and configure reader Set reader = Ci.CreateBarcodePro If Err.Number <> 0 Then WScript.Echo Err.Description : Exit Sub cibfCode39 = 2: cibfCode128 = 4: reader.Type = cibfCode39 + cibfCode128 ' preprocess image editor.Image.Open fileName, page If Err.Number <> 0 Then WScript.Echo Err.Description : Exit Sub editor.AdvancedBinarize 0, 0, 0 ' Read barcodes reader.Image = editor.Image reader.Find 0 If Err.Number <> 0 Then WScript.Echo Err.Description : Exit Sub ' Process results For Each Barcode In reader.Barcodes WScript.Echo Barcode.Text Next End Sub |
Read poor quality barcodes from a file
These examples work with single or multi-page documents read from a file
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
using Inlite.ClearImageNet; // . . . void _PreProcessAndRead(object sender, EditPageEventArgs e) { System.Diagnostics.Debug.WriteLine("Page: " + e.Editor.Image.PageNumber.ToString() + " of " + e.Editor.Image.FileName); // Preprocess page e.Editor.AdvancedBinarize(); // Read barcodes BarcodeReader reader = new BarcodeReader(); reader.Code39 = true; reader.Code128 = true; Barcode[] barcodes = reader.Read(e.Editor); foreach (Barcode bc in barcodes) { System.Diagnostics.Debug.WriteLine(bc.Text); ProcessBarcode(bc); } e.skipPage = true; // Required in ClearImage 7 } void ReadBarcode1DpreProcfromFile(string fileName) { ImageEditor editor = new ImageEditor(); try { bool ret = editor.Edit(fileName, _PreProcessAndRead, "", ImageFileFormat.unknownFormat, true); } catch (FileNotFoundException ex) { ; } // Generated if all pages are skipped, Required in ClearImage 7 catch (ArgumentException ex) { ; } // Generate if output file name is not specified. Required in ClearImage 7 catch (Exception ex) { ProcessException(ex); } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
Imports Inlite.ClearImageNet ' . . . Private Sub _PreProcessAndRead(sender As Object, e As EditPageEventArgs) System.Diagnostics.Debug.WriteLine("Page: " & e.Editor.Image.PageNumber.ToString() & " of " & Convert.ToString(e.Editor.Image.FileName)) ' Preprocess page e.Editor.AdvancedBinarize() ' Read barcodes Dim reader As New BarcodeReader() reader.Code39 = True reader.Code128 = True Dim barcodes As Barcode() = reader.Read(e.Editor) For Each bc As Barcode In barcodes System.Diagnostics.Debug.WriteLine(bc.Text) ProcessBarcode(bc) Next e.skipPage = True' Required in ClearImage 7 End Sub Private Sub ReadBarcode1DpreProcfromFile(fileName As String) Dim editor As New ImageEditor() Try Dim ret As Boolean = editor.Edit(fileName, AddressOf _PreProcessAndRead, "", ImageFileFormat.unknownFormat, True) Catch ex As FileNotFoundException ' Generated if all pages are skipped, Required in ClearImage 7 Catch ex As ArgumentException ' Generate if output file name is not specified. Required in ClearImage 7 Catch ex As Exception ProcessException(ex) End Try End Sub |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void ReadBarcode1DPreProcfromFile (const char *fileName) { try { // configure preprocess ICiRepairPtr editor; editor = Ci->CreateRepair (); // configure reader ICiBarcodeProPtr reader; reader = Ci->CreateBarcodePro (); reader->Type = (FBarcodeType) (cibfCode39 | cibfCode128); int page = 1; while (true) { // Preprocess image editor->Image->Open (_bstr_t(fileName), page); editor->AdvancedBinarize(0, 0, 0); // Read barcode reader->Image = editor->Image; reader->Find(0); for (int i = 1 ; i <= reader->Barcodes->Count ; i++) ProcessBarcode (reader->Barcodes->Item[i]); // got to the next page page++; if (page > reader->Image->PageCount) break; } } catch (_com_error &ex) {ProcessException(ex);} } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
void ReadBarcode1DPreProcfromFile(String fileName) { try { // Create pre-processing ICiRepair editor = Ci.CreateRepair(); // Create and configure barcode reader ICiBarcodePro reader = Ci.CreateBarcodePro(); reader.setType(new FBarcodeType(FBarcodeType.cibfCode39, FBarcodeType.cibfCode128)); // Multiple types int page = 1; while (true) { // Pre-process image editor.getImage().Open(fileName, page); editor.AdvancedBinarize(0, 0, 0); // Read barcodes reader.setImage(editor.getImage()); reader.Find(0); // Process results for (int i = 1; i <= reader.getBarcodes().getCount(); i++) { ICiBarcode Bc = reader.getBarcodes().getItem(i); System.out.println(Bc.getText()); // Use text value OR ProcessBarcode(Bc); // do other processing } page++; if (page > reader.getImage().getPageCount()) break; } } catch (Exception ex) { ProcessException(ex); } finally { // Collect garbage periodically to release JNI-managed objects System.gc(); } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
function ReadBarcode1DfromFile ($fileName) { // Create ClearImage COM Server $Ci = new COM("ClearImage.ClearImage"); // Creat-preprocessor $editor = $Ci->CreateRepair(); // Creare and configure barcode reader $reader = $Ci->CreateBarcodePro(); $cibfCode39 = 2; $cibfCode128 = 4; $reader->Type = $cibfCode39 + $cibfCode128; $page=1; while(true) { // Pre-process image $editor->Image->Open($fileName, $page); $editor->AdvancedBinarize (0,0,0); // Read barcodes $reader->Image = $editor->Image; $BCcount = $reader->Find(0); // Process Results for ($i=1;$i<=$BCcount;$i++) { $Bc = $reader->BarCodes($i); $Bc = $reader->BarCodes($i); echo "$Bc->Text<br>"; } $page++; if ($page > $reader->Image->PageCount) break; } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
procedure ReadBarcode1DPreProcFromFile(const fileName: string); var Ci: ICiServer; reader: ICiBarcodePro; editor: ICiRepair; i: integer; page: integer; begin try try begin //Create and configure reader Ci:=CoCiServer.Create; editor:=Ci.CreateRepair; reader:=Ci.CreateBarcodePro; reader.Type_:=cibCode39 or cibCode128; page:=1; while true do begin // Preprocess Image editor.Image.Open(fileName, page); editor.AdvancedBinarize(0, 0, 0); // Read barcodes reader.Image = editor.Image reader.Find(0); // Process results for i := 1 to reader.Barcodes.Count do begin ShowMessage(reader.Barcodes.Item[i].Text) ; end; page := page + 1; if (page > reader.Image.PageCount) then Break; end; end except on E:Exception do //Process errors ShowMessage(Format('Error:%s.File:%s', [E.Message,FileName])); end finally end end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Sub ReadBarcode1DPreProcfromFile(fileName) On Error Resume Next ' Create pre-processor Set editor = Ci.CreateRepair 'Create CiServer object: Set Ci = CreateObject("ClearImage.ClearImage") ' Create and configure reader Set reader = Ci.CreateBarcodePro If Err.Number <> 0 Then WScript.Echo Err.Description : Exit Sub cibfCode39 = 2: cibfCode128 = 4: reader.Type = cibfCode39 + cibfCode128 page = 1 do ' preprocess image editor.Image.Open fileName, page If Err.Number <> 0 Then WScript.Echo Err.Description : Exit Sub editor.AdvancedBinarize 0, 0, 0 ' Read barcodes reader.Image = editor.Image reader.Find 0 If Err.Number <> 0 Then WScript.Echo Err.Description : Exit Sub ' Process results For Each Barcode In reader.Barcodes WScript.Echo Barcode.Text Next page = page + 1 if (page > reader.Image.PageCount) Then Exit Do Loop End Sub |
Read poor quality barcodes from a stream
These examples work with single or multi-page documents read from a stream.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
using Inlite.ClearImageNet; // . . . void _PreProcessAndRead(object sender, EditPageEventArgs e) { System.Diagnostics.Debug.WriteLine("Page: " + e.Editor.Image.PageNumber.ToString() + " of " + e.Editor.Image.FileName); // Preprocess page e.Editor.AdvancedBinarize(); // Read barcodes BarcodeReader reader = new BarcodeReader(); reader.Code39 = true; reader.Code128 = true; Barcode[] barcodes = reader.Read(e.Editor); foreach (Barcode bc in barcodes) { System.Diagnostics.Debug.WriteLine(bc.Text); ProcessBarcode(bc); } e.skipPage = true; // Required in ClearImage 7 } void ReadBarcode1DpreProcfromStream(Stream stream) { ImageEditor editor = new ImageEditor(); try { editor.Edit(stream, _PreProcessAndRead, ImageFileFormat.unidentified); } catch (Exception ex) { ProcessException(ex); } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
Imports Inlite.ClearImageNet ' . . . Private Sub _PreProcessAndRead(sender As Object, e As EditPageEventArgs) System.Diagnostics.Debug.WriteLine("Page: " & e.Editor.Image.PageNumber.ToString() & " of " & Convert.ToString(e.Editor.Image.FileName)) ' Preprocess page e.Editor.AdvancedBinarize() ' Read barcodes Dim reader As New BarcodeReader() reader.Code39 = True reader.Code128 = True Dim barcodes As Barcode() = reader.Read(e.Editor) For Each bc As Barcode In barcodes System.Diagnostics.Debug.WriteLine(bc.Text) ProcessBarcode(bc) Next e.skipPage = True ' Required in ClearImage 7 End Sub Private Sub ReadBarcode1DpreProcfromStream(stream As Stream) Dim editor As New ImageEditor() Try editor.Edit(stream, AddressOf _PreProcessAndRead, ImageFileFormat.unidentified) Catch ex As Exception ProcessException(ex) End Try End Sub |
How To Improve Barcode Quality
Achieving a high recognition rate of your barcodes is essential for production-quality imaging applications. To reach that high level of recognition it is most important to begin the document cycle with well designed, reliable barcodes. A good design takes into account the available space, the printing or rendering details and the scanning requirements. This presentation discusses the issues to related to design and implementation of reliable barcode-based imaging system.
Optimize your overall process
An important element that affects the recognition rate is the barcode module size. A module is the smallest barcode element (either bar or space) and its dimension is measured in pixels. As the barcode is read, the module size is measured and the value is made available in the CiBarcode.ModuleSize property (COM API) or the Barcode.ModuleSize property (.NET API)
The module size is a calculated as follows:
|
1 |
mod = PrinterModSizeIn * ScannerDPI; Where: |
- PrinterModSizeIn is smallest physical dimension of the module on a printed document measured in inches. This value is typically configured through settings of barcode generator either in milli-inches or in pixels. In the later case value is calculated by dividing pixel size by Printer PDI
- ScannerDPI is the scanning resolution. This value is typically written by scanner into an image property (also called tags) in the image file header. It is is available as the CiImage.HorzDpi and CiImage.VertDpi properties (COM API) or the ImageInfo.HorizontalDpi and the ImageInfo.VerticalDpi properties (.NET API)
- Image scanners usually configure the image resolution from a Preferences user dialog. Most commercial scanning resolutions are set between 150 to 300 dpi.
- FAX machines use standard preset values of 204dpi in the horizontal direction and 196 dpi (fine mode) or 98 dpi (standard mode) in the vertical direction. Consequently, horizontally oriented barcodes survive better on faxed pages.
Images from cameras or cell phones do not have a fixed resolution since they do not use “contact” scanning. These devices often set the resolution tag in the JPG files to 72dpi or 1dpi, but these value do not reflect the actual image resolution. The measured module size can only be obtained from ModuleSize property.
Recommendations:
- Configure your production process (i.e. printing then scanning) to deliver module sizes in the range of:
- 1D and PDF417 barcodes from 3 pixels to 10 pixels
- QR and DataMatrix barcodes from 5 pixels to 15 pixels
- If you use an image scanner and can control the scanning properties:
- Set scanner resolution setting to achieve above desired module size.
- It is preferable to use grayscale scanning, and to NOT use JPEG compression (see below)
- If the image has to be stored as bitonal or with a specific DPI, then use the ClearImage scaling and binarization methods.
Then use ClearImage to save your image in desired format.
- If can control document creation configure barcode generator to achieve above module size, taking in account scanning or faxing resolution.
- Use Inlite’s Barcodes For Documents to optimize barcode generation.
Reduce JPEG compression damages
JPEG Compression is the primary method for saving grayscale and color images. JPEG compressed images can be stored as a single page, JPG file, or as images in multi page TIFF or PDF files. While JPEG compression preserves the general appearance and text readability of the image, it can damage the quality of barcodes. This application note explains these issues in more detail.
Recommendations:
- If the raw color or grayscale image bitmaps from the scanning driver are available in
memory, use them in that form rather than opening the compressed files. - If the color or grayscale images must be saved and read from file, then save them uncompressed (BMP, Uncompressed TIFF) or with loss less compression (LZW). Most SaveAs dialogs offer a selection of file Formats and compression options.
- If JPEG compression is required use the highest quality JPEG setting to save the files. Digital cameras usually offer a menu to set the “Image Quality” to high, expressed in Percent (closer to 100% is better) or as a file size (larger is better). Also, you can use other methods to mitigate the negative effects of JPEG:
- Make the barcode larger.
- Increase scanning resolution.
- Use 2D barcodes to take advantage of their Error Correction capabilities
- For PDF417 – Specify a higher level of ECC to assure sufficient amount of correction data.
- Contact support@inlitersearch.com if you have difficulties with your images.
Contacting Inlite Technical Support
Contact Inlite at support@inliteresearch.com to help you:
- Maximize the number of barcodes recognized
- Optimize the process to achieve maximum throughput.
Please include this information with your support request:
- Representative set of your typical image files (up to 10 files).
Large files (> 1MB) are typically transferred through online file-sharing service (dropbox.com or service of your choice) - Describe you barcode-based process:
- How barcodes are generated?
- How images with barcode are acquired?
- What is your programming language?
- If you already have code developed, send us snippets showing all calls to ClearImage
If possible send us zipped project that can be build and modified in our environment,
