Click here to Skip to main content
11,927,711 members (48,918 online)
Click here to Skip to main content
Add your own
alternative version


122 bookmarked

Computer Vision - Decoding a Morse Code Flashing LED

, 4 Dec 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Using webcam and image processing to decode a Morse code flashing LED.



I hope you all know what Morse code is; for those who are not aware of it, Morse code is a standardized sequence of dots and dashes to represent any character and number. Well, in this article, I'm demonstrating a program that can control an LED connected to the parallel port of my computer and make it flash Morse code. For dots, the LED remains "on" for lesser time than dashes, thus giving an impression of Morse code. Not to forget the most interesting part: the program can use the webcam and a little bit of image processing to make the computer understand the Morse code flashing LED and convert it back to English.

Perhaps you are asking, "What's the use?" Well, no practical use.. but sometimes, coding is just fun. Ever since I made the webcam enabled Tic-Tac-Toe, I was thinking of some unique ways to communicate with my computer using something real and physical.. and ended up writing this program!

Before we begin, I would recommend you to go through I/O Ports Uncensored - 1 - Controlling LEDs (Light Emitting Diodes) with Parallel Port by Levent Saltuklaroglu, and be sure to read the sections on Parallel Ports and Hexadecimal / Decimal / Binary if you haven't already done so, as we will use the same procedure to reach the parallel port in our application.

One really cool thing with this program would be using two computers, one having a parallel port (LED attached) and the other with a USB webcam. Watch this YouTube video and check out this program in action ..

Generating the Morse code (by flashing an LED)


As, you can see in the picture above, the program can control an LED connected to the parallel port of my computer and make it flash Morse code. All I have to do is enter a phrase in English and press the button. It also has an option for putting out the audio depicting Morse code.

First of all, a written text is converted to Morse code by a string extension, and finally, the generated Morse code is used to control the LED and audio part. Check the code snippet below:

static class StringToMorse
    //extension to string
    public static string GetMorseCode(this string str)
        string morse="";
        foreach (char ch in str)
            if (ch == 'a' || ch == 'A')
                morse += ".- ";
            else if (ch == 'b' || ch == 'B')
                morse += "-... ";
            else if (ch == 'c' || ch == 'C')
                morse += "-.-. ";
            // All alphabets not included
            // It'd have made article unnecessarily big..

Now, once the Morse code is generated, the program calls a function asynchronously in a different thread to make the LED flash the Morse without hanging the application. I'm using inpout32.dll to control the parallel port. You can find the complete details about importing and using this DLL in the article I recommended above. Below is a code snippet that uses the generated Morse code to flash the LED:

private void stringToLed(string str)//generated morse code is argument
    foreach (char ch in str)
        int mul_fac = Convert.ToInt16(comboBox1.Text);
        richTextBox1.Text += ch;
        int sleep = Convert.ToInt16(some vaue);//pause between dot and dash
        if (ch == '.')
            PortInterop.Output(888, 255); // set all data pins to 1
            System.Threading.Thread.Sleep(on time of dot);
            PortInterop.Output(888, 0);
        else if (ch == '-')
            PortInterop.Output(888, 255); 
            System.Threading.Thread.Sleep(on time for dash);
            PortInterop.Output(888, 0);
        else if (ch == '/')
            PortInterop.Output(888, 0);// set all data pins to 0
            System.Threading.Thread.Sleep(character pause);
        else if (ch == ' ')
            PortInterop.Output(888, 0); 
            System.Threading.Thread.Sleep(word pause);


Webcam and image processing...

To add more fun, I added another feature of decoding this Morse code. The program watches the on/off sequence of the LED and converts it into English!

Earlier, I was thinking of processing the whole webcam frame and finding the on/ off state of the LED, but this technique made the application work too slow that it couldn't even differentiate between a dot and a dash. So, I made an assumption that the camera source will be stationery, and the user will have to define the light source by a mouse click within the webcam window (see the image below: the point of interception of the two yellow lines is the marker which defines the light source).

Once the light source is defined, the program can go through the pixels near the defined light source and calculate the average brightness of each pixel.

using System.Drawing;
Color c = someBitmap.GetPixel(x,y);
float b = c.GetBrightness();

Wow, that's easy! This code was simple to write, and easy to understand. However, unfortunately, it is very slow. If you use this code, it might take several milliseconds to process, because the GetPixel()/SetPixel() methods are too slow for iterating through bitmaps. So, in this project, we'll make use of the BitmapData class in GDI+ to access the information we want. BitmapData only allows us to access the data it stores through a pointer. This means that we'll have to use the unsafe keyword to scope the block of code which accesses the data. Based on an article by Eric Gunnerson, here's a class which will perform a very quick unsafe image processing:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
public unsafe class UnsafeBitmap
    Bitmap bitmap;
    int width;
    BitmapData bitmapData = null;
    Byte* pBase = null;

    public UnsafeBitmap(Bitmap bitmap)
        this.bitmap = new Bitmap(bitmap);

    public UnsafeBitmap(int width, int height)
        this.bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);

    public void Dispose()

    public Bitmap Bitmap
            return (bitmap);

    public struct PixelData
        public byte blue;
        public byte green;
        public byte red;

    private Point PixelSize
            GraphicsUnit unit = GraphicsUnit.Pixel;
            RectangleF bounds = bitmap.GetBounds(ref unit);

            return new Point((int)bounds.Width, (int)bounds.Height);

    public void LockBitmap()
        GraphicsUnit unit = GraphicsUnit.Pixel;

        RectangleF boundsF = bitmap.GetBounds(ref unit);
        Rectangle bounds = new Rectangle((int)boundsF.X, (int)boundsF.Y, 
                           (int)boundsF.Width, (int)boundsF.Height);

        width = (int)boundsF.Width * sizeof(PixelData);

        if (width % 4 != 0)
            width = 4 * (width / 4 + 1);

        bitmapData = bitmap.LockBits(bounds, ImageLockMode.ReadWrite, 
        pBase = (Byte*)bitmapData.Scan0.ToPointer();

    public PixelData GetPixel(int x, int y)
        PixelData returnValue = *PixelAt(x, y);
        return returnValue;

    public void SetPixel(int x, int y, PixelData colour)
        PixelData* pixel = PixelAt(x, y);
        *pixel = colour;

    public void UnlockBitmap()
        bitmapData = null;
        pBase = null;

    public PixelData* PixelAt(int x, int y)
        return (PixelData*)(pBase + y * width + x * sizeof(PixelData));

Be sure to check Eric's article on unsafe image processing. This class can be used for retrieving the red, green, and blue values of any pixel, as shown below:

private void GetBritnessOnce(ref Bitmap image)
    // This code is for getting brighness only once !!
    // pt is point defining light source
    Rectangle rect = new Rectangle(pt.X - 3, pt.Y - 3, 6, 6);
    //cropping image withing boundaries of this rectangle
    Bitmap img = image.Clone(rect, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    UnsafeBitmap uBitmap = new UnsafeBitmap(img);//unsafe bitmap class
    float avgBritness = 0;
    for (int x = 0; x < 6; x++)
         for (int y = 0; y < 6; y++)
              byte red, green, blue;
              red = uBitmap.GetPixel(x, y).red;
              green = uBitmap.GetPixel(x, y).green;
              blue = uBitmap.GetPixel(x, y).blue;
              avgBritness += (299 * red + 587 * green + 114 * blue) / 1000;
              // brightness function
     avgBritness /= 36 ;
     label19.Text = Convert.ToString(avgBritness);

With the brightness value, the program can find whether the light source is "on" or "off", and with a stop watch, the timings of on/ off sequences could be calculated...

The program provides all the stats below the webcam view, and with these stats, it also predicts the Morse code! Make sure to watch the video above.

Some notes on using the software

Well, the most tricky part is adjusting the settings within the program to make it work perfectly.. Let's start with the settings one by one..

Here, "dot" defines the time span for which the LED will remain on for every dot within the Morse code, and "DMF", by default, is 3, which means the time span for every dash in the Morse code will be "dot" * 3.

Let's suppose we need to define " ._ " by flashing LEDs. How will we do that..?

LED on for "LESS time" --> LED off for "SOME time" --> LED on for "MORE time"

This LED off for "SOME time" is what "Imm" is in the above settings.

Now, let's come to the settings for the decoding part. I'll soon add some AI so that the program will adapt itself after collecting some on/off data. For now, let me tell you the significance of each setting:

For brightness less than the "Brightness Threshold", the light source will be considered "off". For best results, keep this setting only a little less than the brightness of the light source in "on" state. Similarly, you can play with other settings to get the best results. The program will provide all the statistics below the webcam window.


We have reached the end of this article, and I hope you enjoyed reading it. Now, here's some homework for you: try implementing features like AI for the program, and make this program self-adaptive according to its environment. Use your ideas, and if you end up doing something cool, I'd love to hear about it. Smile | :) Have fun!


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Canada Canada
cat me.txt

You may also be interested in...

Comments and Discussions

GeneralMy vote of 5 Pin
Amir Mohammad Nasrollahi9-Aug-13 21:20
professionalAmir Mohammad Nasrollahi9-Aug-13 21:20 
QuestionBits per second Pin
vlcreceiver3-Mar-13 16:02
membervlcreceiver3-Mar-13 16:02 
GeneralMy vote of 5 Pin
Mario Majčica22-Aug-12 4:17
memberMario Majčica22-Aug-12 4:17 
GeneralMy vote of 5 Pin
Steve Maier5-Jun-12 7:58
memberSteve Maier5-Jun-12 7:58 
GeneralMy vote of 3 Pin
FrewCen17-May-12 8:12
memberFrewCen17-May-12 8:12 
GeneralRe: My vote of 3 Pin
Shivamkalra25-Aug-12 19:21
memberShivamkalra25-Aug-12 19:21 
QuestionSuper Cool! Pin
Patrick Harris19-Feb-12 16:41
memberPatrick Harris19-Feb-12 16:41 
AnswerRe: Super Cool! Pin
PHS24116-Apr-12 22:49
memberPHS24116-Apr-12 22:49 
GeneralMy vote of 5 Pin
Sergio Andrés Gutiérrez Rojas20-Nov-11 21:20
memberSergio Andrés Gutiérrez Rojas20-Nov-11 21:20 
GeneralMy vote of 5 Pin
Sergio Andrés Gutiérrez Rojas13-Sep-11 20:23
memberSergio Andrés Gutiérrez Rojas13-Sep-11 20:23 
GeneralMy vote of 5 Pin
Mika Wendelius17-Jul-11 2:27
memberMika Wendelius17-Jul-11 2:27 
GeneralExcellent posting Pin
frito_burrito8-Nov-10 11:11
memberfrito_burrito8-Nov-10 11:11 
GeneralMy vote of 5 Pin
frito_burrito8-Nov-10 11:04
memberfrito_burrito8-Nov-10 11:04 
GeneralVery nice article Pin
Jorg DD17-Dec-09 3:28
memberJorg DD17-Dec-09 3:28 
GeneralRe: Very nice article Pin
shivam_kalra17-Dec-09 4:22
membershivam_kalra17-Dec-09 4:22 
GeneralInterestring Pin
Miller45-Dec-09 8:43
memberMiller45-Dec-09 8:43 
GeneralRe: Interestring Pin
shivam_kalra5-Dec-09 9:39
membershivam_kalra5-Dec-09 9:39 
GeneralRe: Interestring Pin
RyanEK6-Dec-09 12:07
memberRyanEK6-Dec-09 12:07 
GeneralRe: Interestring Pin
Homncruse8-Dec-09 7:58
memberHomncruse8-Dec-09 7:58 
GeneralRe: Interestring Pin
shivam_kalra8-Dec-09 8:04
membershivam_kalra8-Dec-09 8:04 
GeneralRe: Interestring Pin
Homncruse8-Dec-09 8:29
memberHomncruse8-Dec-09 8:29 
GeneralRe: Interestring Pin
Andrew Rissing8-Dec-09 9:04
memberAndrew Rissing8-Dec-09 9:04 
GeneralRe: Interestring Pin
Miller48-Dec-09 9:25
memberMiller48-Dec-09 9:25 
GeneralRe: Interestring Pin
Andrew Rissing8-Dec-09 11:17
memberAndrew Rissing8-Dec-09 11:17 
GeneralRe: Interestring Pin
Andrew Rissing8-Dec-09 11:23
memberAndrew Rissing8-Dec-09 11:23 
GeneralRe: Interestring Pin
shivam_kalra9-Dec-09 5:35
membershivam_kalra9-Dec-09 5:35 
Thank you so much, this really helped..I added
ch = char.ToLower(ch);

which removed all the 26 additional cases..
GeneralRe: Interestring Pin
kornman009-Dec-09 5:11
memberkornman009-Dec-09 5:11 
GeneralRe: Interestring Pin
Miller48-Dec-09 9:34
memberMiller48-Dec-09 9:34 
GeneralRe: Interestring Pin
Homncruse8-Dec-09 10:38
memberHomncruse8-Dec-09 10:38 
GeneralGreat ! Pin
Salvatore Tipaldi5-Dec-09 4:18
memberSalvatore Tipaldi5-Dec-09 4:18 
GeneralRe: Great ! Pin
shivam_kalra5-Dec-09 4:41
membershivam_kalra5-Dec-09 4:41 
GeneralThis is awesome (as was your tic-tac-toe one) Pin
Sacha Barber5-Dec-09 1:17
mvpSacha Barber5-Dec-09 1:17 
GeneralRe: This is awesome (as was your tic-tac-toe one) Pin
shivam_kalra5-Dec-09 1:32
membershivam_kalra5-Dec-09 1:32 
GeneralReally neat !! Pin
catzarecute4-Dec-09 12:01
membercatzarecute4-Dec-09 12:01 
GeneralRe: Really neat !! Pin
shivam_kalra4-Dec-09 14:25
membershivam_kalra4-Dec-09 14:25 
GeneralWonderful Pin
Marcelo Ricardo de Oliveira4-Dec-09 3:46
memberMarcelo Ricardo de Oliveira4-Dec-09 3:46 
GeneralRe: Wonderful Pin
shivam_kalra4-Dec-09 4:01
membershivam_kalra4-Dec-09 4:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.151126.1 | Last Updated 4 Dec 2009
Article Copyright 2009 by shivamkalra
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid