Transym OCR & Peter–Proof Of Concept completed and approved

This week’s work comprised of readying the DnlCore-Shared.OCR classes, the DnlCore-Shared.ImageViewer control… and then build the Indexing Client Proof of Concept on top of that.

I wanted to prove that I could create a Win32 form with the following:

  • a custom PictureBox-like control that would allow
    • scrolling, and (more importantly)
    • zooming in the scrolled-out image representation, while preserving the image itself at its original resolution
    • selecting an area and returning the contents of the selection rectangle (from the preserved image to retain full scan resolution) as a bitmap
  • the facility to OCR the result of selected areas and use these to populate a selected field
  • the facility to load image files from a TWAIN scanner or from disk.

<echo>This has now been accomplished. </echo>

It goes like so:
The client (with the scanned image loaded at the right):
image

We start out at a zoom level of 33%, which is perfectly readable on a screen that was cutting-edge 10 years ago.

We doubleclick the Sender field (which makes it go powder blue) and select the name of the sender:
image

and then we choose Selection/OCR (or we hit Alt-O because we are lazy), and lo and behold:
image

We double-click the Reference field, draw an area around the 5928 bit, Alt-O, and hey look here:image

The main work went into creating the image viewer control (read: stealing the image control from the sample viewer application that comes with Transym OCR, and poking it in the eye until it does what I want). It inherits from the PictureBox control and now (after some eye-poking) adds the functionality mentioned in the requirements above.

A fair bit of work went into isolating the API code of TOCR and the creating the .NET wrapper around it.

The actual logic in this POC amounts to only 208 lines of generously formatted C#. I reckon I could compress that to something like 150 lines, but that would be at the expense of code readability and not add a bit to performance or ease of maintenance.
Of course, as PoC’s come, it contains very little exception handling, so it’ll probably grow – but still… impressive!

(“Jeez, I’m good!”) Winking smile

The indexing client logic all 202 lines of it:

using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using DnlCore.Shared;
using DnlCore.Shared.OCR;

namespace pocIndexing
{
    public partial class Form1 : Form
    {
        private Bitmap[] bitmaps;
        private bool haveSelection = false;
        private Rectangle selection;
        private string currentFile;
        private System.Windows.Forms.TextBox selectedControl = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void LoadFromFileButton_Click(object sender, EventArgs e)
        {
            if (LoadFromFileDialog.ShowDialog() == DialogResult.OK)
            {
                currentFile = LoadFromFileDialog.FileName;
                loadImage(currentFile);
                setZoomRate(33);
            }
        }

        private void loadImage(string filePath)
        {
            editableImage1.Image = new Bitmap(filePath);
            setZoomRate(33);
        }

        private void ScanButton_Click(object sender, EventArgs e)
        {
            loadFromScanner();
        }

        private void loadFromScanner()
        {
            IEngine engine = new Engine();
            bitmaps = engine.GetDocumentFromScanner();
            editableImage1.Image = bitmaps[0];
        }

        private void setZoomRate(float rate)
        {
            editableImage1.Zoom = rate;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            editableImage1.Dispose();
        }

        private void page_SelectionChanged(Rectangle rect)
        {
            if (rect.Width != 0)
            {
                imageToolStripMenuItem.Text = "&Selection";
                selection = rect;
                haveSelection = true;
            }
            else
            {
                imageToolStripMenuItem.Text = "&Image";
                haveSelection = false;
            }
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (LoadFromFileDialog.ShowDialog() == DialogResult.OK)
            {
                loadImage(LoadFromFileDialog.FileName);
            }
        }

        private void scanToolStripMenuItem_Click(object sender, EventArgs e)
        {
            loadFromScanner();
        }

        private void zoom20MenuItem_Click(object sender, EventArgs e)
        {
            setZoomRate(20);
        }

        private void zoom33MenuItem_Click(object sender, EventArgs e)
        {
            setZoomRate(33);
        }

        private void zoom50MenuItem_Click(object sender, EventArgs e)
        {
            setZoomRate(50);
        }

        private void zoom75MenuItem_Click(object sender, EventArgs e)
        {
            setZoomRate(75);
        }

        private void zoom100MenuItem_Click(object sender, EventArgs e)
        {
            setZoomRate(100);
        }

        private string getOcrResult()
        {
            string result = string.Empty;
            IEngine ocrEngine = new Engine();
            Bitmap bitmapToOcr = null;
            if (!haveSelection)
            {
                bitmapToOcr = editableImage1.Image;
            }
            else
            {
                bitmapToOcr = editableImage1.SelectionImage;
            }
            result = ocrEngine.GetOcrFromBitmap(bitmapToOcr);

            return result;
        }

        private string detectFileType(string filePath)
        {
            string result = string.Empty;
            // Magic numbers embedded in files - these are mutually exclusive
            const short BMP_ID = 0x4D42;                // bitmap fileheader file type
            const short TIF_BO_LE = 0x4949;             //TIFF byte order little endian
            const short TIF_BO_BE = 0x4d4d;             // TIFF byte order big endian
            const short TIF_ID_LE = 0x2a;               // TIFF version little endian
            const short TIF_ID_BE = 0x2a00;             // TIFF version big endian
            const short GIF_ID1 = 0x4947;               // GIF 1st short
            const short GIF_ID2 = 0x3846;               // GIF 2nd short

            short value;

            using (System.IO.BinaryReader reader = new System.IO.BinaryReader(System.IO.File.Open(filePath, System.IO.FileMode.Open)))
            {
                value = reader.ReadInt16();
                switch (value)
                {
                    case BMP_ID:
                        result = "BMP";
                        break;
                    case TIF_BO_LE:
                    case TIF_BO_BE:
                    case TIF_ID_BE:
                    case TIF_ID_LE:
                        result = "TIF";
                        break;
                    case GIF_ID1:
                    case GIF_ID2:
                        result = "GIF";
                        break;
                    default:
                        result = "";
                        break;
                }
                reader.Close();
            }

            return result;
        }

        private void oCRToolStripMenuItem_Click(object sender, EventArgs e)
        {
            selectedControl.Text = getOcrResult();
            selectedControl.BackColor = Color.White;
        }

        private void textBox_DoubleClick(object sender, EventArgs e)
        {
            selectedControl = (TextBox)sender;
            selectedControl.BackColor = Color.PowderBlue;
            foreach (var t in this.Controls)
            {
                if (t.GetType() == typeof(TextBox) && t != sender)
                {
                    TextBox tb = (TextBox)t;
                    tb.BackColor = Color.White;
                }
            }
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            TextBox t = (TextBox)sender;
            t.BackColor = Color.White;
        }

    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *