How much banking has become absurd (at least for individual customers)

Spending most of my life in a cash-only economy and only got touch to the modern word of banking for the last few years opened my eyes on a range of problems. I once thought banks was a great idea, giving risk management tasks to the most capable people, earning me money while I rest instead of having them just lying around; I don’t need to carry cash and don’t have to worry about thugs rampaging the streets; and I have nothing to lose, banks shouldn’t charge fee to keep your money, after all, I am giving them my money, and banks ought to be nice to me or I will go somewhere else.

And now I think it’s a ridiculous idea. Really!

They do charge you money for keeping your money, lots of it

I work all week, and when I went out to play in the weekend, I find myself short on cash and some small shops I often visit on those trip don’t like cards very much (reason to be covered later), I came to the ATM to withdraw my money, and greeted with a screen that I will be charged extra fees because I’m withdrawing on a public holiday.

Seriously?

It’s understandable when you have to pay fees withdrawing from other banks than the bank you made the card with because of connection fees and such (to be frank, banks should be able to deal out this with each other without inconveniencing the customer, since it’s their responsibility), but charging their own customer for taking their own money? What kind of world am I living in? I can’t use my money without paying fees? Why am I depositing in the first place? I can just keep all my money under my pillow and use them any good day I want! It’s not like they are hiring someone to stand at each ATM booth, counting and handling the money to you at weekends when their money-crunching networks went out for no reason on weekends! Everything is automated, run 24/24 (Wait, that’s not true either); machines don’t have days off (they don’t know better) and the banks don’t have to pay machines extra for working on holidays, so why are the fees?

They have a huge sum of interests from our money to begin with, and they just can’t offset the operating cost themselves? If they just can’t take care of the money well enough that it cover their operating cost, why do it anyways?

Remember when I said some shops don’t like cards? Oh yes, they charge the business owner fees for having customer use their money. Are you kidding me? Why get everyone to use your stupid blood-sucking credit cards in the first place? We’ve had better time dealing with each other for free you know?

Overdrafts and other hideous practices

Things doesn’t go well for the poor, and if you have a low balance on your card, well, you are as good as bankrupt.

The good thing when keeping your own money is, you can’t spend what you don’t have? You thought that was a bad thing? Let’s say that you purchased lots of things with your credit card, you thought “hey, may be it will just overdrawn a bit, I will be able to pay it back with no trouble!” and your check came several days late and boom! Overdraft fees, processing fees, interest, stacked up all at the same time, and the best part: they compound with a rate much higher than the rate you got with a positive balance!

If you think using a check card or debit card is fine because in theory, you can’t come into overdraft, right?

As you read, in theory. Did you read that you can’t overspent with these cards in the card contract? I guess not! In the real world and not Wonderland, they allow you to go on a spending spree all you want, and at the end of the month send you a short notice that you owe them money, plus interest, and fees, and congratulates you on having the equivalent functions of a credit card to mess your life up!

Banks in the US even charge something called “overdraft protection fee”, which is basically another way of sucking your blood to the last drop: you think you are safe when one account goes into the red and having two accounts to manage your funds will protect you? Wrong! With this great service, you can pay them money to have all your accounts linked together  and uses your money to cover the overdraft. That is, when you really ran out of money you can still fall into the overdraft trap!

LOL

Judging the main cause of the last global financial crisis is fat bellies selling debt to each other and got off on our tax money with big bonuses, I think now they have become the last people on the world that you should ever give your money to!

(Very) crude .NET wrapper for Tesseract 3

I was searching for a quick way to recognize license plates and stumbled upon EmguCV’s sample code. But this code requires Tesseract OCR (An open source OCR engine currently sponsored by Google) and its .NET wrapper. Unfortunately, this code was written when Tesseract was still at version 2, so is the .NET wrapper so none of them will work for me (I need Tesseract 3 to use a new language supported by this version), worse still, the original wrapper developer has stated that he won’t touch Tesseract code again, so the only way to proceed would be to write my own wrapper.

As you may have know, writing and debugging a wrapper for another language is a tedious and painstaking process, so I opted for the easy way out: use Tesseract command line version to recognize text without digging into the source code.

And here’s the result: A wrapper class for Tesseract on .NET – it will write your bitmap to a temporary file, execute Tesseract and extract the result back into the code. Note that this will run very slow, and that’s why I say it’s crude, a better way would be to analyze Tesseract code and write an interop DLL, but i just don’t have time for that 🙂

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;

namespace PlateDetection
{
    class Tesseract
    {
        string Language = "eng";
        const string Program = @"C:Program Files (x86)Tesseract-OCRtesseract";

        /// <summary>
        /// Changes language
        /// </summary>
        /// <param name="language">Language code</param>
        /// <param name="NotUsed">Not used but kept for compatibitily with Tesseract.net old wrapper</param>
        internal void Init(string language, bool NotUsed)
        {
            Language = language;
        }

        /// <summary>
        /// Main function, recognize any text in the bitmap
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rectangle">Not used, you may change that easily by cutting the bitmap</param>
        /// <returns></returns>
        internal List<string> DoOCR(System.Drawing.Bitmap bmp, System.Drawing.Rectangle rectangle)
        {
            bmp.Save("temp.png", System.Drawing.Imaging.ImageFormat.Png);
            System.Diagnostics.Process.Start(Program, "temp.png temp -l " + Language);
            
            string [] Words = new string[0];
            // The application may need some time to process, file is not ready yet,
            // or the application may still writing
            while (true)
            {
                try
                {
                    Words = File.ReadAllText("temp.txt").Split(new char[] { ' ', 'r', 'n', 't' }, StringSplitOptions.RemoveEmptyEntries);
                    break;
                }
                catch
                {

                }
            }
            File.Delete("temp.png");
            File.Delete("temp.txt");
            List<string> Result = new List<string>(Words);
            return Result;
        }

        /// <summary>
        /// Currently not used as there is no allocation done
        /// </summary>
        internal void Dispose()
        {
            
        }
    }
}

And here’s the full plate detection class

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.Util;
using System.Diagnostics;
using Emgu.CV.Structure;

namespace PlateDetection
{
    /// <summary>
    /// A license plate detector
    /// </summary>
    public class LicensePlateDetector : DisposableObject
    {

        private Tesseract _ocr;

        /// <summary>
        /// Create a license plate detector
        /// </summary>
        public LicensePlateDetector()
        {
            //create OCR
            _ocr = new Tesseract();

            //You can download more language definition data from
            //http://code.google.com/p/tesseract-ocr/downloads/list
            //Languages supported includes:
            //Dutch, Spanish, German, Italian, French and English
            _ocr.Init("eng", false);
        }

        /// <summary>
        /// Create a license plate detector
        /// </summary>
        public LicensePlateDetector(string language)
        {
            //create OCR
            _ocr = new Tesseract();

            //You can download more language definition data from
            //http://code.google.com/p/tesseract-ocr/downloads/list
            //Languages supported includes:
            //Dutch, Spanish, German, Italian, French and English
            _ocr.Init(language, false);
        }

        /// <summary>
        /// Detect license plate from the given image
        /// </summary>
        /// <param name="img">The image to search license plate from</param>
        /// <param name="licensePlateList">A list of images where the detected license plate region is stored</param>
        /// <param name="filteredLicensePlateList">A list of images where the detected license plate region with noise removed is stored</param>
        /// <param name="boxList">A list where the region of license plate, defined by an MCvBox2D is stored</param>
        /// <returns>The list of words for each license plate</returns>
        public List<List<string>> DetectLicensePlate(Image<Bgr, byte> img, List<Image<Gray, Byte>> licensePlateList, List<Image<Gray, Byte>> filteredLicensePlateList, List<MCvBox2D> boxList)
        {
            //Stopwatch w = Stopwatch.StartNew();
            List<List<string>> licenses = new List<List<string>>();
            using (Image<Gray, byte> gray = img.Convert<Gray, Byte>())
            using (Image<Gray, Byte> canny = new Image<Gray, byte>(gray.Size))
            using (MemStorage stor = new MemStorage())
            {
                CvInvoke.cvCanny(gray, canny, 100, 50, 3);

                Contour<Point> contours = canny.FindContours(
                     Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
                     Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE,
                     stor);
                FindLicensePlate(contours, gray, canny, licensePlateList, filteredLicensePlateList, boxList, licenses);
            }
            //w.Stop();
            return licenses;
        }

        private void FindLicensePlate(
           Contour<Point> contours, Image<Gray, Byte> gray, Image<Gray, Byte> canny,
           List<Image<Gray, Byte>> licensePlateList, List<Image<Gray, Byte>> filteredLicensePlateList, List<MCvBox2D> boxList,
           List<List<string>> licenses)
        {
            for (; contours != null; contours = contours.HNext)
            {
                Contour<Point> approxContour = contours.ApproxPoly(contours.Perimeter * 0.05, contours.Storage);

                if (approxContour.Area > 100 && approxContour.Total == 4)
                {
                    //img.Draw(contours, new Bgr(Color.Red), 1);
                    if (!IsParallelogram(approxContour.ToArray()))
                    {
                        Contour<Point> child = contours.VNext;
                        if (child != null)
                            FindLicensePlate(child, gray, canny, licensePlateList, filteredLicensePlateList, boxList, licenses);
                        continue;
                    }

                    MCvBox2D box = approxContour.GetMinAreaRect();

                    double whRatio = (double)box.size.Width / box.size.Height;
                    if (!(3.0 < whRatio && whRatio < 8.0))
                    {
                        Contour<Point> child = contours.VNext;
                        if (child != null)
                            FindLicensePlate(child, gray, canny, licensePlateList, filteredLicensePlateList, boxList, licenses);
                        continue;
                    }

                    Image<Gray, Byte> plate = gray.Copy(box);
                    Image<Gray, Byte> filteredPlate = FilterPlate(plate);

                    List<string> words;
                    using (Bitmap bmp = filteredPlate.Bitmap)
                        words = _ocr.DoOCR(bmp, filteredPlate.ROI);

                    licenses.Add(words);
                    licensePlateList.Add(plate);
                    filteredLicensePlateList.Add(filteredPlate);
                    boxList.Add(box);
                }
            }
        }

        /// <summary>
        /// Check if the four points forms a parallelogram
        /// </summary>
        /// <param name="pts">The four points that defines a polygon</param>
        /// <returns>True if the four points defines a parallelogram</returns>
        private static bool IsParallelogram(Point[] pts)
        {
            LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

            double diff1 = Math.Abs(edges[0].Length - edges[2].Length);
            double diff2 = Math.Abs(edges[1].Length - edges[3].Length);
            if (diff1 / edges[0].Length <= 0.05 && diff1 / edges[2].Length <= 0.05
               && diff2 / edges[1].Length <= 0.05 && diff2 / edges[3].Length <= 0.05)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Filter the license plate to remove noise
        /// </summary>
        /// <param name="plate">The license plate image</param>
        /// <returns>License plate image without the noise</returns>
        private static Image<Gray, Byte> FilterPlate(Image<Gray, Byte> plate)
        {
            Image<Gray, Byte> thresh = plate.ThresholdBinaryInv(new Gray(120), new Gray(255));

            using (Image<Gray, Byte> plateMask = new Image<Gray, byte>(plate.Size))
            using (Image<Gray, Byte> plateCanny = plate.Canny(new Gray(100), new Gray(50)))
            using (MemStorage stor = new MemStorage())
            {
                plateMask.SetValue(255.0);
                for (
                   Contour<Point> contours = plateCanny.FindContours(
                      Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
                      Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL,
                      stor);
                   contours != null; contours = contours.HNext)
                {
                    Rectangle rect = contours.BoundingRectangle;
                    if (rect.Height > (plate.Height >> 1))
                    {
                        rect.X -= 1; rect.Y -= 1; rect.Width += 2; rect.Height += 2;
                        rect.Intersect(plate.ROI);

                        plateMask.Draw(rect, new Gray(0.0), -1);
                    }
                }

                thresh.SetValue(0, plateMask);
            }

            thresh._Erode(1);
            thresh._Dilate(1);

            return thresh;
        }

        protected override void DisposeObject()
        {
            _ocr.Dispose();
        }
    }
}

Seeing is believing, here’s a sample of the algorithm run on a live feed off the camera:

And even though I say it’s slow, it’s still fast enough to process my movements and only goes into recognition when the plate is detected, so the performance is acceptable anyways :). The code above is for anyone who just want to test the algorithm to decide whether or not they will use it, I don’t think it’s a good idea to put these into production code 🙂

Why specifying the call type when performing P/Invoke is important

I was working on a C# project that required interaction between unmanaged and managed code. It’s nothing complex, just calling various functions in a DLL through a wrapper class, then I hit this exception:

A call to PInvoke function 'TestLPRLib!EvAPI.EvLicensePlateReg::evLPROpen' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

Strange thing is, this “stack unbalanced” exception only pop up when running in debug mode, the application still work just fine without debugging, but leave bugs in applications is not my habit, so I decided to reach out to Google. The unmanaged code used __declspec(dllexport) to specify exported functions, internet forums suggested this exception is caused by the difference in calling convention.

So, I changed the unmanaged code from

extern "C" __declspec(dllexport) int evLPROpen(int width, int height, int widthStep, int depth = 8, int channel = 3);

to

extern "C" __declspec(dllexport) int __cdecl evLPROpen(int width, int height, int widthStep, int depth = 8, int channel = 3);

And the wrapper from

[DllImport("LicensePlateRecognitionLib.dll")]
public static extern int evLPROpen(int width, int height, int widthStep, int depth, int channel);

to

[DllImport("LicensePlateRecognitionLib.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int evLPROpen(int width, int height, int widthStep, int depth, int channel);

(using the clear stack by caller convention), and the exception goes away. By default, .NET assumes you use __stdcall, but I didn’t have much success trying this 🙂