Wednesday, 20 November 2013

QBitmap - MUCH faster image processing [C#]

Posted By: Unknown - 05:59
As the GetPixel and SetPixel functions provided by the .NET framework are (so) slow, I had to write my own implementation of it. This Bitmap class allows you to access image pixels directly from the memory (via unsafe code.) Trust me, this is way faster!

Features:
  • Way faster than the core framework's implementation. (~ 10 times faster!)
  • Supports Format32bppArgb, Format24bppRgb.

To-do list:
  • Support for more image formats.
  • Suggestions?

Here it is, QBitmap.cs:
// http://codecav.blogspot.in/
public unsafe class QBitmap : IDisposable
    {
        private Bitmap _bitmap;
        private BitmapData _bitmapData;
 
        public int Width { get; private set; }
 
        public int Height { get; private set; }
 
        public PixelFormat PixelFormat { get; private set; }
 
        public QBitmap(Bitmap bitmap)
        {
            _bitmap = bitmap;
 
            Initialize();
        }
 
        private void Initialize()
        {
            if (_bitmap == null)
                throw new ArgumentNullException();
 
            Width = _bitmap.Width;
            Height = _bitmap.Height;
            PixelFormat = _bitmap.PixelFormat;
        }
 
        public void LockBits()
        {
            try
            {
                _bitmapData = _bitmap.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadWrite, PixelFormat);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }
 
        public void UnlockBits()
        {
            try
            {
                _bitmap.UnlockBits(_bitmapData);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }
 
        public Color GetPixel(int x, int y)
        {
            byte a = 0, r, g, b;
            var pData = (byte*) _bitmapData.Scan0.ToPointer();
 
            switch (PixelFormat)
            {
                case PixelFormat.Format32bppArgb:
                    b = *(pData + (y * _bitmapData.Stride) + (x * 4));
                    g = *(pData + (y * _bitmapData.Stride) + (x * 4) + 1);
                    r = *(pData + (y * _bitmapData.Stride) + (x * 4) + 2);
                    a = *(pData + (y * _bitmapData.Stride) + (x * 4) + 3);
                    break;
 
                case PixelFormat.Format24bppRgb:
                    b = *(pData + (y * _bitmapData.Stride) + (x * 3));
                    g = *(pData + (y * _bitmapData.Stride) + (x * 3) + 1);
                    r = *(pData + (y * _bitmapData.Stride) + (x * 3) + 2);
                    break;
 
                // Support for more formats.
                case PixelFormat.Format8bppIndexed:
                case PixelFormat.Format4bppIndexed:
                case PixelFormat.Format1bppIndexed:
                default:
                    throw new NotSupportedException("The specified pixel format is not supported.");
            }
 
            return Color.FromArgb(a, r, g, b);
        }
 
        public void SetPixel(int x, int y, Color color)
        {
            var pData = (byte*) _bitmapData.Scan0.ToPointer();
 
            switch (PixelFormat)
            {
                case PixelFormat.Format32bppArgb:
                    *(pData + (y*_bitmapData.Stride) + (x*4)) = color.B;
                    *(pData + (y*_bitmapData.Stride) + (x*4) + 1) = color.G;
                    *(pData + (y*_bitmapData.Stride) + (x*4) + 2) = color.R;
                    *(pData + (y*_bitmapData.Stride) + (x*4) + 3) = color.A;
                    break;
 
                case PixelFormat.Format24bppRgb:
                    *(pData + (y*_bitmapData.Stride) + (x*3)) = color.B;
                    *(pData + (y*_bitmapData.Stride) + (x*3) + 1) = color.G;
                    *(pData + (y*_bitmapData.Stride) + (x*3) + 2) = color.R;
                    break;
 
                // Support for more formats.
                case PixelFormat.Format8bppIndexed:
                case PixelFormat.Format4bppIndexed:
                case PixelFormat.Format1bppIndexed:
                default:
                    throw new NotSupportedException("The specified pixel format is not supported.");
            }
        }
 
        private bool _disposed;
        public void Dispose()
        {
            if (_disposed)
                return;
 
            _bitmap.Dispose();
            _bitmap = null;
 
            _disposed = true;
        }
    }

Usage:
// QBitmap usage (http://codecav.blogspot.in/)
 
var image = (Bitmap) Image.FromFile("Path\To\Image.png");
 
using (var qBitmap = new QBitmap(image))
{
        qBitmap.LockBits();
 
        // Perform operations on the image.
 
        qBitmap.UnlockBits();
}

Feel free to ask/suggest anything. Have fun with it!

Tuesday, 19 November 2013

Sudoku Solver - Backtracking Algorithm

Posted By: Unknown - 03:09
I coded this a few weeks ago, so here it is. A simple, neat and highly customizable Sudoku solver (with a simple board) in C# based on the Backtracking algorithm.

In action:
Sudoku solver in C#
Sudoku solver in C#
And here's the source code
  • SudukoBoard.cs:
    // SudukoBoard.cs public class SudokuBoard : GroupBox { public sealed class SudokuCell : TextBox { public int X { get; private set; } public int Y { get; private set; } ///  /// Thread-safe Get/Set Text. ///  public string Value { get { string text = string.Empty; Invoke(new MethodInvoker(delegate { text = Text; })); return text; } set { Invoke(new MethodInvoker(delegate { Text = value; })); } } // Ctor. public SudokuCell(int x, int y, int width, int height) { X = x; Y = y; Width = width; Height = height; TextAlign = HorizontalAlignment.Center; Font = new Font("Verdana", 8, FontStyle.Regular); } // Only digits (except 0) are allowed. protected override void OnKeyPress(KeyPressEventArgs e) { if (char.IsWhiteSpace(e.KeyChar) || (!char.IsDigit(e.KeyChar) && e.KeyChar != (char) 8) || e.KeyChar == (char) 48) e.Handled = true; base.OnKeyPress(e); } } // An array representing the board. private readonly SudokuCell[,] _cell; private readonly int _cellWidth, _cellHeight; // Current board values. All operations/verifications are performed on this array. public readonly string[,] CurrBoardValues; public SudokuCell this[int x, int y] { get { return _cell[x, y]; } } // Ctor. public SudokuBoard(int cellWidth, int cellHeight) { _cellWidth = cellHeight; _cellHeight = cellHeight; Width = cellWidth * 9 + 20; Height = cellHeight * 9 + 28; _cell = new SudokuCell[9, 9]; CurrBoardValues = new string[9, 9]; CreateBoardControl(); } // Creates the actual sudoku board control (an array of SudokuCell in a GroupBox.) private void CreateBoardControl() { int extraSpaceX, extraSpaceY = 0; for (int y = 0; y < 9; y++) { extraSpaceX = 0; if (y != 0 && y % 3 == 0) extraSpaceY += 3; for (int x = 0; x < 9; x++) { if (x != 0 && x % 3 == 0) extraSpaceX += 3; _cell[x, y] = new SudokuCell(x, y, _cellHeight, _cellHeight) { MaxLength = 1, Location = new Point(Location.X + 7 + x * _cellWidth + extraSpaceX, Location.Y + 15 + y * _cellHeight + extraSpaceY) }; _cell[x, y].TextChanged += OnSudokuCellTextChanged; Controls.Add(_cell[x, y]); } } } private void OnSudokuCellTextChanged(object sender, EventArgs eventArgs) { var cell = (SudokuCell) sender; TryMove(cell); } #region Public methods public void ClearBoard() { for (int y = 0; y < 9; y++) { for (int x = 0; x < 9; x++) { _cell[x, y].Value = CurrBoardValues[x, y] = string.Empty; } } } public void UpdateBoard() { for (int y = 0; y < 9; y++) { for (int x = 0; x < 9; x++) { _cell[x, y].Value = CurrBoardValues[x, y]; } } } public bool TryMove(SudokuCell cell) { return TryMove(cell.X, cell.Y, cell.Value); } public bool TryMove(int x, int y, string value) { CurrBoardValues[x, y] = value; /* !IsMoveAllowedInRow(x, y, value) || !IsMoveAllowedInColumn(x, y, value) || !IsMoveAllowedInCurrentBox(x, y, value) */ if (!IsMoveAllowed(x, y, value)) { _cell[x, y].Value = string.Empty; CurrBoardValues[x, y] = string.Empty; return false; } return true; } #endregion #region Rules public bool IsMoveAllowed(int x, int y, string value) { return IsMoveAllowedInRow(x, y, value) && IsMoveAllowedInColumn(x, y, value) && IsMoveAllowedInCurrentBox(x, y, value); } private bool IsMoveAllowedInRow(int x, int y, string value) { for (int i = 0; i < 9; i++) { if (i == x) continue; if (CurrBoardValues[i, y] == value) return false; } return true; } private bool IsMoveAllowedInColumn(int x, int y, string value) { for (int i = 0; i < 9; i++) { if (i == y) continue; if (CurrBoardValues[x, i] == value) return false; } return true; } private bool IsMoveAllowedInCurrentBox(int x, int y, string value) { int boxX = 0, boxY = 0; if (x > 5) boxX = 6; else if (x > 2) boxX = 3; if (y > 5) boxY = 6; else if (y > 2) boxY = 3; for (int j = 0; j < 3; j++) { for (int i = 0; i < 3; i++) { if (boxX + i == x && boxY + j == y) continue; if (CurrBoardValues[boxX + i, boxY + j] == value) return false; } } return true; } #endregion } 
  • SudokuSolver.cs:
    // SudokuSolver.cs public class SudokuSolver { private int _numOfTrials; private readonly SudokuBoard _board; private readonly AutoResetEvent _solvedEvent; public SudokuSolver(SudokuBoard board) { _board = board; _numOfTrials = 0; _solvedEvent = new AutoResetEvent(false); } #region Solve public void Solve() { var thread = new Thread(StartBacktrackSolver) { IsBackground = true }; thread.Start(); // Waiting for the solver thread to solve. _solvedEvent.WaitOne(); _board.UpdateBoard(); OnGameCompleted(_numOfTrials); _numOfTrials = 0; } private void StartBacktrackSolver() { BacktrackSolver(0, 0); _solvedEvent.Set(); } // Backtrack solving algorithm. private bool BacktrackSolver(int x, int y) { if (x == 9) { if (y == 8) return true; // game solved! x = 0; y++; } // pre-solved value. if (!string.IsNullOrEmpty(_board.CurrBoardValues[x, y])) { return BacktrackSolver(x + 1, y); } // Trying every possible (1 - 9) value on each cell. for (int i = 1; i < 10; i++) { if (_board.IsMoveAllowed(x, y, i.ToString())) { _numOfTrials++; _board.CurrBoardValues[x, y] = i.ToString(); // Moving on to the next cell. if (BacktrackSolver(x + 1, y)) return true; // game solved. // DEAD END reached. } } // DEAD END (no move was possible!) _board.CurrBoardValues[x, y] = string.Empty; return false; } #endregion public delegate void GameCompletedHandler(int numOfTrials); public event GameCompletedHandler GameCompleted; private void OnGameCompleted(int numOfTrials) { if (GameCompleted != null) GameCompleted(numOfTrials); } }

Wanna try it? Here's just the executable:
http://www.mediafire.com/?umzad2b7jsgyjcg

Found a bug or something? Let me know.
Enjoy! ;)


Copyright © 2013 Coding The Void™ is a registered trademark.

Designed by Templateism. Hosted on Blogger Platform.