79776905

Date: 2025-09-27 17:05:32
Score: 0.5
Natty:
Report link

... here now follow the classes for the ChainCode, add them to your project

(Code for the WinForm in the last answer):

Make sure, you reference the following namespaces:

using System.Collections;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
    //    This class implements a chaincode finder (crack code), as an adaption of 
    //    V. Kovalevsky's crack-code development.  
    //    (PLEASE NOTE THAT THESE ARE *NOT* HTTPS CONNECTIONS! It's an old web-site.)
    //       http://www.miszalok.de/Samples/CV/ChainCode/chain_code.htm and
    //       http://www.miszalok.de/Lectures/L08_ComputerVision/CrackCode/CrackCode_d.htm (german only). See also
    //       http://www.miszalok.de/Samples/CV/ChainCode/chaincode_kovalev_e.htm
    //As the name crackcode says, we are moving on the (invisible) "cracks" in between the pixels to find the outlines of objects

    //Please note that I dont use the /unsafe switch and byte-pointers, since I dont know, whether you are allowed to use that in your code...
    public class ChainFinder
    {
        private int _threshold = 0;
        private bool _nullCells = false;
        private Point _start = new Point(0, 0);
        private int _height = 0;

        public bool AllowNullCells
        {
            get
            {
                return _nullCells;
            }
            set
            {
                _nullCells = value;
            }
        }

        public List<ChainCode>? GetOutline(Bitmap bmp, int threshold, bool grayscale, int range, bool excludeInnerOutlines, int initialValueToCheck, bool doReverse)
        {
            BitArray? fbits = null; //Array to hold the information about processed pixels
            _threshold = threshold;
            _height = bmp.Height;

            try
            {
                List<ChainCode> fList = new List<ChainCode>();

                //Please note that the bitarray is one "column" larger than the bitmap's width
                fbits = new BitArray((bmp.Width + 1) * bmp.Height, false);

                //is the condition so, that the collected coordinate's pixel/color channel values are greater than the threshold,
                //or lower (then use the reversed switch, maybe with an approppriate initial value set)
                if (doReverse)
                    FindChainCodeRev(bmp, fList, fbits, grayscale, range, excludeInnerOutlines, initialValueToCheck);
                else
                    FindChainCode(bmp, fList, fbits, grayscale, range, excludeInnerOutlines, initialValueToCheck);

                return fList;
            }
            catch /*(Exception exc)*/
            {
                if (fbits != null)
                    fbits = null;
            }

            return null;
        }

        // PLEASE NOTE THAT THIS IS *NOT* A HTTPS CONNECTION! It's an old web-site.
        // Adaption von Herrn Prof. Dr.Ing. Dr.med. Volkmar Miszalok, siehe: http://www.miszalok.de/Samples/CV/ChainCode/chain_code.htm
        private void FindChainCode(Bitmap b, List<ChainCode> fList, BitArray fbits, bool grayscale, int range, bool excludeInnerOutlines, int initialValueToCheck)
        {
            SByte[,] Negative = new SByte[,] { { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } };
            SByte[,] Positive = new SByte[,] { { 0, 0 }, { -1, 0 }, { -1, -1 }, { 0, -1 } };

            Point LeftInFront = new Point();
            Point RightInFront = new Point();
            bool LeftInFrontGreaterTh;
            bool RightInFrontGreaterTh;
            int direction = 1;

            BitmapData? bmData = null;

            //if (!AvailMem.AvailMem.checkAvailRam(b.Width * b.Height * 4L))
            //    return;

            try
            {
                bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                int stride = bmData.Stride;

                //copy the BitmapBits to a byte-array for processing
                byte[]? p = new byte[(bmData.Stride * bmData.Height) - 1 + 1];
                Marshal.Copy(bmData.Scan0, p, 0, p.Length);

                while (start_crack_search(bmData, p, fbits, grayscale, range, initialValueToCheck))
                {
                    //setup and add the first found pixel to our results list
                    ChainCode cc = new ChainCode();

                    cc.start = _start;
                    // cc.Coord.Add(_start)

                    int x = _start.X;
                    int y = _start.Y + 1;
                    direction = 1;

                    cc.Chain.Add(direction);

                    //as long as we have not reached the starting pixel again, do processing steps
                    while (x != _start.X || y != _start.Y)
                    {
                        LeftInFront.X = x + Negative[direction, 0];
                        LeftInFront.Y = y + Negative[direction, 1];
                        RightInFront.X = x + Positive[direction, 0];
                        RightInFront.Y = y + Positive[direction, 1];

                        //add the correct pixel
                        switch (direction)
                        {
                            case 0:
                                {
                                    cc.Coord.Add(new Point(LeftInFront.X - 1, LeftInFront.Y));
                                    break;
                                }

                            case 1:
                                {
                                    cc.Coord.Add(new Point(LeftInFront.X, LeftInFront.Y - 1));
                                    break;
                                }

                            case 2:
                                {
                                    cc.Coord.Add(new Point(LeftInFront.X + 1, LeftInFront.Y));
                                    break;
                                }

                            case 3:
                                {
                                    cc.Coord.Add(new Point(LeftInFront.X, LeftInFront.Y + 1));
                                    break;
                                }
                        }

                        //now do the core algorithm steps, description above
                        LeftInFrontGreaterTh = false;
                        RightInFrontGreaterTh = false;

                        if (LeftInFront.X >= 0 && LeftInFront.X < b.Width && LeftInFront.Y >= 0 && LeftInFront.Y < b.Height)
                        {
                            if (!grayscale)
                                LeftInFrontGreaterTh = p[LeftInFront.Y * stride + LeftInFront.X * 4 + 3] > _threshold;
                            else if (range > 0)
                                LeftInFrontGreaterTh = ((p[LeftInFront.Y * stride + LeftInFront.X * 4] > _threshold) && (p[LeftInFront.Y * stride + LeftInFront.X * 4] <= _threshold + range));
                            else
                                LeftInFrontGreaterTh = p[LeftInFront.Y * stride + LeftInFront.X * 4] > _threshold;
                        }

                        if (RightInFront.X >= 0 && RightInFront.X < b.Width && RightInFront.Y >= 0 && RightInFront.Y < b.Height)
                        {
                            if (!grayscale)
                                RightInFrontGreaterTh = p[RightInFront.Y * stride + RightInFront.X * 4 + 3] > _threshold;
                            else if (range > 0)
                                RightInFrontGreaterTh = ((p[RightInFront.Y * stride + RightInFront.X * 4] > _threshold) && (p[RightInFront.Y * stride + RightInFront.X * 4] <= _threshold + range));
                            else
                                RightInFrontGreaterTh = p[RightInFront.Y * stride + RightInFront.X * 4] > _threshold;
                        }

                        //set new direction (3 cases, but only 2 of them change the direction
                        //(LeftInFrontGreaterTh + !RightInFrontGreaterTh = move straight on))
                        if (RightInFrontGreaterTh && (LeftInFrontGreaterTh || _nullCells))
                            direction = (direction + 1) % 4;
                        else if (!LeftInFrontGreaterTh && (!RightInFrontGreaterTh || !_nullCells))
                            direction = (direction + 3) % 4;

                        cc.Chain.Add(direction);

                        // fbits (always record upper pixel)
                        switch (direction)
                        {
                            case 0:
                                {
                                    x += 1;
                                    cc.Area += y;
                                    break;
                                }

                            case 1:
                                {
                                    y += 1;
                                    fbits.Set((y - 1) * (b.Width + 1) + x, true);
                                    break;
                                }

                            case 2:
                                {
                                    x -= 1;
                                    cc.Area -= y;
                                    break;
                                }

                            case 3:
                                {
                                    y -= 1;
                                    fbits.Set(y * (b.Width + 1) + x, true);
                                    break;
                                }
                        }

                        //if we finally reach the starting pixel again, add a final coord and chain-direction if one of the distance-constraints below is met.
                        //This happens always due to the setup of the algorithm (adding the coord to the ChainCode for the last set direction)
                        if (x == _start.X && y == _start.Y)
                        {
                            if (Math.Abs(cc.Coord[cc.Coord.Count - 1].X - x) > 1 || Math.Abs(cc.Coord[cc.Coord.Count - 1].Y - y) > 1)
                            {
                                if (Math.Abs(cc.Coord[cc.Coord.Count - 1].X - x) > 1)
                                {
                                    cc.Coord.Add(new Point(cc.Coord[cc.Coord.Count - 1].X + 1, cc.Coord[cc.Coord.Count - 1].Y));
                                    cc.Chain.Add(0);
                                }
                                if (Math.Abs(cc.Coord[cc.Coord.Count - 1].Y - y) > 1)
                                {
                                    cc.Coord.Add(new Point(cc.Coord[cc.Coord.Count - 1].X, cc.Coord[cc.Coord.Count - 1].Y + 1));
                                    cc.Chain.Add(1);
                                }
                                break;
                            }
                        }
                    }

                    bool isInnerOutline = false;

                    if (excludeInnerOutlines)
                    {
                        if (cc.Chain[cc.Chain.Count - 1] == 0)
                        {
                            isInnerOutline = true;
                            break;
                        }
                    }

                    //add the list to the results list
                    if (!isInnerOutline)
                    {
                        cc.Coord.Add(_start);
                        fList.Add(cc);
                    }
                }

                p = null;
                b.UnlockBits(bmData);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);

                try
                {
                    if(bmData != null)
                        b.UnlockBits(bmData);
                }
                catch
                {
                }
            }
        }

        private bool start_crack_search(BitmapData bmData, byte[] p, BitArray fbits, bool grayscale, int range, int initialValueToCheck)
        {
            int left = 0;
            int stride = bmData.Stride;

            for (int y = _start.Y; y <= bmData.Height - 1; y++)
            {
                for (int x = 0; x <= bmData.Width - 1; x++)
                {
                    if (x > 0)
                    {
                        if (!grayscale)
                            left = p[y * stride + (x - 1) * 4 + 3];
                        else
                            left = p[y * stride + (x - 1) * 4];
                    }
                    else
                        left = initialValueToCheck;

                    if (!grayscale)
                    {
                        if ((left <= _threshold) && (p[y * stride + x * 4 + 3] > _threshold) && (fbits.Get(y * (bmData.Width + 1) + x) == false))
                        {
                            _start.X = x;
                            _start.Y = y;
                            fbits.Set(y * (bmData.Width + 1) + x, true);
                            //OnProgressPlus();
                            return true;
                        }
                    }
                    else if (range > 0)
                    {
                        if ((left <= _threshold) && (p[y * stride + x * 4] > _threshold) && (p[y * stride + x * 4] <= _threshold + range) && (fbits.Get(y * (bmData.Width + 1) + x) == false))
                        {
                            _start.X = x;
                            _start.Y = y;
                            fbits.Set(y * (bmData.Width + 1) + x, true);
                            //OnProgressPlus();
                            return true;
                        }
                    }
                    else if ((left <= _threshold) && (p[y * stride + x * 4] > _threshold) && (fbits.Get(y * (bmData.Width + 1) + x) == false))
                    {
                        _start.X = x;
                        _start.Y = y;
                        fbits.Set(y * (bmData.Width + 1) + x, true);
                        //OnProgressPlus();
                        return true;
                    }
                }
            }
            return false;
        }

        // PLEASE NOTE THAT THIS IS *NOT* A HTTPS CONNECTION! It's an old web-site.
        // Adaption von Herrn Prof. Dr.Ing. Dr.med. Volkmar Miszalok, siehe: http://www.miszalok.de/Samples/CV/ChainCode/chain_code.htm
        private void FindChainCodeRev(Bitmap b, List<ChainCode> fList, BitArray fbits, bool grayscale, int range, bool excludeInnerOutlines, int initialValueToCheck)
        {
            SByte[,] Negative = new SByte[,] { { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } };
            SByte[,] Positive = new SByte[,] { { 0, 0 }, { -1, 0 }, { -1, -1 }, { 0, -1 } };

            Point LeftInFront = new Point();
            Point RightInFront = new Point();
            bool LeftInFrontGreaterTh;
            bool RightInFrontGreaterTh;
            int direction = 1;

            BitmapData? bmData = null;

            //if (!AvailMem.AvailMem.checkAvailRam(b.Width * b.Height * 4L))
            //    return;

            try
            {
                bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                int stride = bmData.Stride;

                byte[]? p = new byte[(bmData.Stride * bmData.Height) - 1 + 1];
                Marshal.Copy(bmData.Scan0, p, 0, p.Length);

                while (start_crack_searchRev(bmData, p, fbits, grayscale, range, initialValueToCheck))
                {
                    ChainCode cc = new ChainCode();

                    cc.start = _start;

                    int x = _start.X;
                    int y = _start.Y + 1;
                    direction = 1;

                    cc.Chain.Add(direction);

                    while (x != _start.X || y != _start.Y)
                    {
                        LeftInFront.X = x + Negative[direction, 0];
                        LeftInFront.Y = y + Negative[direction, 1];
                        RightInFront.X = x + Positive[direction, 0];
                        RightInFront.Y = y + Positive[direction, 1];

                        switch (direction)
                        {
                            case 0:
                                {
                                    cc.Coord.Add(new Point(LeftInFront.X - 1, LeftInFront.Y));
                                    break;
                                }

                            case 1:
                                {
                                    cc.Coord.Add(new Point(LeftInFront.X, LeftInFront.Y - 1));
                                    break;
                                }

                            case 2:
                                {
                                    cc.Coord.Add(new Point(LeftInFront.X + 1, LeftInFront.Y));
                                    break;
                                }

                            case 3:
                                {
                                    cc.Coord.Add(new Point(LeftInFront.X, LeftInFront.Y + 1));
                                    break;
                                }
                        }

                        LeftInFrontGreaterTh = false;
                        RightInFrontGreaterTh = false;

                        if (LeftInFront.X >= 0 && LeftInFront.X < b.Width && LeftInFront.Y >= 0 && LeftInFront.Y < b.Height)
                        {
                            if (!grayscale)
                                LeftInFrontGreaterTh = p[LeftInFront.Y * stride + LeftInFront.X * 4 + 3] < _threshold;
                            else if (range > 0)
                                LeftInFrontGreaterTh = ((p[LeftInFront.Y * stride + LeftInFront.X * 4] < _threshold) && (p[LeftInFront.Y * stride + LeftInFront.X * 4] >= _threshold + range));
                            else
                                LeftInFrontGreaterTh = p[LeftInFront.Y * stride + LeftInFront.X * 4] < _threshold;
                        }

                        if (RightInFront.X >= 0 && RightInFront.X < b.Width && RightInFront.Y >= 0 && RightInFront.Y < b.Height)
                        {
                            if (!grayscale)
                                RightInFrontGreaterTh = p[RightInFront.Y * stride + RightInFront.X * 4 + 3] < _threshold;
                            else if (range > 0)
                                RightInFrontGreaterTh = ((p[RightInFront.Y * stride + RightInFront.X * 4] < _threshold) && (p[RightInFront.Y * stride + RightInFront.X * 4] >= _threshold + range));
                            else
                                RightInFrontGreaterTh = p[RightInFront.Y * stride + RightInFront.X * 4] < _threshold;
                        }

                        if (RightInFrontGreaterTh && (LeftInFrontGreaterTh || _nullCells))
                            direction = (direction + 1) % 4;
                        else if (!LeftInFrontGreaterTh && (!RightInFrontGreaterTh || !_nullCells))
                            direction = (direction + 3) % 4;

                        cc.Chain.Add(direction);

                        // fbits (immer oberen punkt aufzeichnen)
                        switch (direction)
                        {
                            case 0:
                                {
                                    x += 1;
                                    cc.Area += y;
                                    break;
                                }

                            case 1:
                                {
                                    y += 1;
                                    fbits.Set((y - 1) * (b.Width + 1) + x, true);
                                    break;
                                }

                            case 2:
                                {
                                    x -= 1;
                                    cc.Area -= y;
                                    break;
                                }

                            case 3:
                                {
                                    y -= 1;
                                    fbits.Set(y * (b.Width + 1) + x, true);
                                    break;
                                }
                        }

                        if (x == _start.X && y == _start.Y)
                        {
                            if (Math.Abs(cc.Coord[cc.Coord.Count - 1].X - x) > 1 || Math.Abs(cc.Coord[cc.Coord.Count - 1].Y - y) > 1)
                            {
                                if (Math.Abs(cc.Coord[cc.Coord.Count - 1].X - x) > 1)
                                {
                                    cc.Coord.Add(new Point(cc.Coord[cc.Coord.Count - 1].X + 1, cc.Coord[cc.Coord.Count - 1].Y));
                                    cc.Chain.Add(0);
                                }
                                if (Math.Abs(cc.Coord[cc.Coord.Count - 1].Y - y) > 1)
                                {
                                    cc.Coord.Add(new Point(cc.Coord[cc.Coord.Count - 1].X, cc.Coord[cc.Coord.Count - 1].Y + 1));
                                    cc.Chain.Add(1);
                                }
                                break;
                            }
                        }
                    }

                    bool isInnerOutline = false;

                    if (excludeInnerOutlines)
                    {
                        if (cc.Chain[cc.Chain.Count - 1] == 0)
                        {
                            isInnerOutline = true;
                            break;
                        }
                    }

                    if (!isInnerOutline)
                    {
                        cc.Coord.Add(_start);
                        fList.Add(cc);
                    }
                }

                p = null;
                b.UnlockBits(bmData);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);

                try
                {
                    if (bmData != null)
                        b.UnlockBits(bmData);
                }
                catch
                {
                }
            }
        }

        private bool start_crack_searchRev(BitmapData bmData, byte[] p, BitArray fbits, bool grayscale, int range, int initialValueToCheck)
        {
            int left = 0;
            int stride = bmData.Stride;

            for (int y = _start.Y; y <= bmData.Height - 1; y++)
            {
                for (int x = 0; x <= bmData.Width - 1; x++)
                {
                    if (x > 0)
                    {
                        if (!grayscale)
                            left = p[y * stride + (x - 1) * 4 + 3];
                        else
                            left = p[y * stride + (x - 1) * 4];
                    }
                    else
                        left = initialValueToCheck;

                    if (!grayscale)
                    {
                        if ((left >= _threshold) && (p[y * stride + x * 4 + 3] < _threshold) && (fbits.Get(y * (bmData.Width + 1) + x) == false))
                        {
                            _start.X = x;
                            _start.Y = y;
                            fbits.Set(y * (bmData.Width + 1) + x, true);
                            //OnProgressPlus();
                            return true;
                        }
                    }
                    else if (range > 0)
                    {
                        if ((left >= _threshold) && (p[y * stride + x * 4] < _threshold) && (p[y * stride + x * 4] >= _threshold + range) && (fbits.Get(y * (bmData.Width + 1) + x) == false))
                        {
                            _start.X = x;
                            _start.Y = y;
                            fbits.Set(y * (bmData.Width + 1) + x, true);
                            //OnProgressPlus();
                            return true;
                        }
                    }
                    else if ((left >= _threshold) && (p[y * stride + x * 4] < _threshold) && (fbits.Get(y * (bmData.Width + 1) + x) == false))
                    {
                        _start.X = x;
                        _start.Y = y;
                        fbits.Set(y * (bmData.Width + 1) + x, true);
                        //OnProgressPlus();
                        return true;
                    }
                }
            }
            return false;
        }

        public void Reset()
        {
            this._start = new Point(0, 0);
        }
    }

    public class ChainCode
    {
        public static int F { get; set; }

        public Point start
        {
            get
            {
                return m_start;
            }
            set
            {
                m_start = value;
            }
        }
        private Point m_start;

        private List<Point> _coord = new List<Point>();
        private List<int> _chain = new List<int>();

        public List<Point> Coord
        {
            get
            {
                return _coord;
            }
            set
            {
                _coord = value;
            }
        }
        public List<int> Chain
        {
            get
            {
                return _chain;
            }
            set
            {
                _chain = value;
            }
        }

        public int Area
        {
            get
            {
                return m_Area;
            }
            set
            {
                m_Area = value;
            }
        }
        private int m_Area;
        private int _id;

        public int Perimeter
        {
            get
            {
                return _chain.Count;
            }
        }

        public int ID
        {
            get
            {
                return this._id;
            }
        }

        public void SetId()
        {
            if (ChainCode.F < Int32.MaxValue)
            {
                ChainCode.F += 1;
                this._id = ChainCode.F;
            }
            else
                throw new OverflowException("The type of the field for storing the ID reports an overflow error.");
        }

        public void ResetID()
        {
            ChainCode.F = 0;
        }

        public ChainCode()
        {
        }

        public override string ToString()
        {
            return "x = " + start.X.ToString() + "; y = " + start.Y.ToString() + "; count = " + _coord.Count.ToString() + "; area = " + this.Area.ToString();
        }
    }

Regards,
Thorsten

Reasons:
  • Blacklisted phrase (1): Regards
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (1):
Posted by: Thorsten Gudera