0

我有一个手写调查的位图对象(见下面的调查图片),其中包含各种复选框。我正在使用一种算法来比较空白、未标记复选框的位图与同一复选框(可能会或可能不会被标记)的位图,以确定复选框是否已被标记。

是否可以将主要调查位图分解为较小的位图对象?例如,对于下面的问题 14,我想在给定每个复选框的 startX、endX、startY 和 endY 位置的情况下用红色方块制作更小的 Bitmap 对象。

在此处输入图像描述

4

1 回答 1

1

我正在开发一个名为 Transparency Maker 的开源程序,它可以帮助您做到这一点,或者至少为您提供一些入门指南。

我的项目读取位图(.png 或 .jpg)并创建一个像素数据库。实际上它是一个 PixelInformation 对象的列表。

LoadPixelDatabase 方法

我的 Windows 窗体应用程序有一个名为 Canvas 的 PictureBox 用于背景图像。您可以轻松地将图像作为参数传递。

using System.Drawing;
using System.Drawing.Imaging;

public void LoadPixelDatabase()
{
    // if we do not have a BackgroundImage yet
    if (this.Canvas.BackgroundImage == null)
    {
        // to do: Show message
        // bail
        return;
    }

    // Create a Bitmap from the Source image
    Bitmap source = new Bitmap(this.Canvas.BackgroundImage);

    // Code To Lockbits
    BitmapData bitmapData = source.LockBits(new Rectangle(0, 0, source.Width, 
    source.Height), ImageLockMode.ReadWrite, source.PixelFormat);
    IntPtr pointer = bitmapData.Scan0;
    int size = Math.Abs(bitmapData.Stride) * source.Height;
    byte[] pixels = new byte[size];
    Marshal.Copy(pointer, pixels, 0, size);

    // End Code To Lockbits

    // Marshal.Copy(pixels,0,pointer, size);
    source.UnlockBits(bitmapData);

    // test only
    int length = pixels.Length;

    // Create a new instance of a 'PixelDatabase' object.
    this.PixelDatabase = new PixelDatabase();

    // locals
    Color color = Color.FromArgb(0, 0, 0);
    int red = 0;
    int green = 0;
    int blue = 0;
    int alpha = 0;

    // variables to hold height and width
    int width = source.Width;
    int height = source.Height;
    int x = -1;
    int y = 0;

    // Iterating the pixel array, every 4th byte is a new pixel,faster than GetPixel
    for (int a = 0; a < pixels.Length; a = a + 4)
    {
        // increment the value for x
        x++;

        // every new column
        if (x >= width)
        {
            // reset x
            x = 0;

            // Increment the value for y
            y++;
        }      

        // get the values for r, g, and blue
        blue = pixels[a];
        green = pixels[a + 1];
        red = pixels[a + 2];
        alpha = pixels[a + 3];

        // create a color
        color = Color.FromArgb(alpha, red, green, blue);

        // Add this point
        PixelInformation pixelInformation = this.PixelDatabase.AddPixel(color, x, y);
    }

    // Create a DirectBitmap
    this.DirectBitmap = new DirectBitmap(source.Width, source.Height);

    // Now we must copy over the Pixels from the PixelDatabase to the DirectBitmap
    if ((this.HasPixelDatabase) && (ListHelper.HasOneOrMoreItems(this.PixelDatabase.Pixels)))
    {
        // iterate the pixels
        foreach (PixelInformation pixel in this.PixelDatabase.Pixels)
        {
            // Set the pixel at this spot
            DirectBitmap.SetPixel(pixel.X, pixel.Y, pixel.Color);
        }
    }
}

我的应用程序可以帮助您的方式是,加载 PixelDatabase 后,您可以执行 LinqQueries,例如:

// Get the pixels in the X range
pixels = pixels.Where(x => x.X >= MinValue && x.X <= MaxValue).ToList();

// Get the Y range
pixels = pixels.Where(x => x.Y >= MinValue && x.Y <= MaxValue).ToList();

(我知道上面可以写成 1 行,这里很难发布)。

获得像素后,您可以创建新图像:

Image image = new Bitmap(width, height);

为新图像创建另一个 DirectBitmap,然后将上述查询中的像素复制到新图像中并保存。

// Save the bitmap
bitmap.Save(fileName);

像素数据库.cs

#region using statements

using DataJuggler.Core.UltimateHelper;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

#endregion

namespace TransparencyMaker.Objects
{

    #region class PixelDatabase
    /// <summary>
    /// This class represents a collection of PixelInformation objects
    /// </summary>
    public class PixelDatabase
    {

        #region Private Variables
        private List<PixelInformation> pixels;
        #endregion

        #region Constructor
        /// <summary>
        /// Create a new instance of a PixelDatabase object
        /// </summary>
        public PixelDatabase()
        {
            // Create a new collection of 'PixelInformation' objects.
            this.Pixels = new List<PixelInformation>();
        }
        #endregion

        #region Methods

            #region AddPixel(Color color, int x, int y)
            /// <summary>
            /// method returns the Pixel
            /// </summary>
            public PixelInformation AddPixel(Color color, int x, int y)
            {  
                // Create a pixe
                PixelInformation pixel = new PixelInformation();

                // Set the color
                pixel.Color = color;

                // Set the values for x and y
                pixel.X = x;
                pixel.Y = y;

                /// The Index is set before the count increments when this item is added
                pixel.Index = this.Pixels.Count;

                // Add this pixel
                this.Pixels.Add(pixel);

                // return value
                return pixel;
            }
            #endregion

        #endregion

        #region Properties

            #region HasOneOrMorePixels
            /// <summary>
            /// This property returns true if this object has one or more 'Pixels'.
            /// </summary>
            public bool HasOneOrMorePixels
            {
                get
                {
                    // initial value
                    bool hasOneOrMorePixels = ((this.HasPixels) && (this.Pixels.Count > 0));

                    // return value
                    return hasOneOrMorePixels;
                }
            }
            #endregion

            #region HasPixels
            /// <summary>
            /// This property returns true if this object has a 'Pixels'.
            /// </summary>
            public bool HasPixels
            {
                get
                {
                    // initial value
                    bool hasPixels = (this.Pixels != null);

                    // return value
                    return hasPixels;
                }
            }
            #endregion

            #region Pixels
            /// <summary>
            /// This property gets or sets the value for 'Pixels'.
            /// </summary>
            public List<PixelInformation> Pixels
            {
                get { return pixels; }
                set { pixels = value; }
            }
            #endregion

        #endregion

    }
    #endregion

}

像素信息.cs

#region using statements

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

#endregion

namespace TransparencyMaker.Objects
{

    #region class PixelInformation
    /// <summary>
    /// This class is used to contain information about a pixel, and 
    /// </summary>
    public class PixelInformation
    {

        #region Private Variables
        private int index;
        private Color color;
        private int x;
        private int y;
        #endregion

        #region Constructor
        /// <summary>
        /// Create a new instance of a PixelInformationObject
        /// </summary>
        public PixelInformation()
        {
            // Perform initializations for this object
            Init();
        }
        #endregion

        #region Methods

            #region Init()
            /// <summary>
            /// This method performs initializations for this object.
            /// </summary>
            public void Init()
            {
            }
            #endregion

            #region ToString()
            /// <summary>
            /// method returns the String
            /// </summary>
            public override string ToString()
            {
                // Create a new instance of a 'StringBuilder' object.
                StringBuilder sb = new StringBuilder();

                // Append the string
                sb.Append("R:");
                sb.Append(Red);
                sb.Append("G:");
                sb.Append(Green);
                sb.Append("B:");
                sb.Append(Blue);
                sb.Append("T:");
                sb.Append(Total);

                // set the return value
                string toString = sb.ToString();

                // return value
                return toString;
            }
            #endregion

        #endregion

        #region Properties

            #region Alpha
            /// <summary>
            /// This property gets or sets the value for 'Alpha'.
            /// </summary>
            public int Alpha
            {
                get 
                { 
                    // initial value
                    int alpha = Color.A;

                    // return value
                    return alpha; 
                }
            }
            #endregion

            #region Blue
            /// <summary>
            /// This property gets or sets the value for 'Blue'.
            /// </summary>
            public int Blue
            {
                get 
                { 
                    // initial value
                    int blue = Color.B;

                    // return value
                    return blue; 
                }
            }
            #endregion

            #region BlueGreen
            /// <summary>
            /// This read only property returns the value for 'BlueGreen'.
            /// </summary>
            public int BlueGreen
            {
                get
                {
                    // initial value
                    int blueGreen = Blue + Green;

                    // return value
                    return blueGreen;
                }
            }
            #endregion

            #region BlueRed
            /// <summary>
            /// This read only property returns the value for 'BlueRed'.
            /// </summary>
            public int BlueRed
            {
                get
                {
                    // initial value
                    int blueRed = Blue + Red;

                    // return value
                    return blueRed;
                }
            }
            #endregion

            #region Color
            /// <summary>
            /// This property gets or sets the value for 'Color'.
            /// </summary>
            public Color Color
            {
                get { return color; }
                set { color = value; }
            }
            #endregion

            #region Green
            /// <summary>
            /// This property gets or sets the value for 'Green'.
            /// </summary>
            public int Green
            {
                get 
                { 
                    // initial value
                    int green = Color.G;

                    // return value
                    return green;
                }
            }
            #endregion

            #region GreenRed
            /// <summary>
            /// This read only property returns the value for 'GreenRed'.
            /// </summary>
            public int GreenRed
            {
                get
                {
                    // initial value
                    int greenRed = Green + Red;

                    // return value
                    return greenRed;
                }
            }
            #endregion

            #region Index
            /// <summary>
            /// This property gets or sets the value for 'Index'.
            /// </summary>
            public int Index
            {
                get { return index; }
                set { index = value; }
            }
            #endregion

            #region Red
            /// <summary>
            /// This property gets or sets the value for 'Red'.
            /// </summary>
            public int Red
            {
                get
                {
                    // initial value
                    int red = this.Color.R;

                    // return value
                    return red;
                }
            }
            #endregion

            #region Total
            /// <summary>
            /// This read only property returns the value for 'Total'.
            /// </summary>
            public int Total
            {
                get
                {
                    // initial value
                    int total = Red + Green + Blue;

                    // return value
                    return total;
                }
            }
            #endregion

            #region X
            /// <summary>
            /// This property gets or sets the value for 'X'.
            /// </summary>
            public int X
            {
                get { return x; }
                set { x = value; }
            }
            #endregion

            #region Y
            /// <summary>
            /// This property gets or sets the value for 'Y'.
            /// </summary>
            public int Y
            {
                get { return y; }
                set { y = value; }
            }
            #endregion

        #endregion

    }
    #endregion

}

DirectBitmap.cs

这里的这个类叫做 DirectBitmap,不是我写的,但我希望我知道作者是谁,以表扬他们,因为它大大加快了我的应用程序的速度。

#region using statements

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using TransparencyMaker.Objects;

#endregion

namespace TransparencyMaker.Util
{

    #region class DirectBitmap
    /// <summary>
    /// This class is used as a faster alternative to GetPixel and SetPixel
    /// </summary>
    public class DirectBitmap : IDisposable
    {

        #region Private Variables
        private History history;
        #endregion

        #region Constructor
        /// <summary>
        /// Create a new instance of a 'DirectBitmap' object.
        /// </summary>
        public DirectBitmap(int width, int height)
        {
            Width = width;
            Height = height;
            Bits = new Int32[width * height];
            BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
            Bitmap = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject());
        }
        #endregion

        #region Methods

            #region Dispose()
            /// <summary>
            /// method Dispose
            /// </summary>
            public void Dispose()
            {
                if (Disposed) return;
                Disposed = true;
                Bitmap.Dispose();
                BitsHandle.Free();
            }
            #endregion

            #region GetPixel(int x, int y)
            /// <summary>
            /// method Get Pixel
            /// </summary>
            public Color GetPixel(int x, int y)
            {
                int index = x + (y * Width);
                int col = Bits[index];
                Color result = Color.FromArgb(col);

                return result;
            }
            #endregion

            #region HandleHistory(int x, int y, Guid historyId, Color previousColor)
            /// <summary>
            /// This method Handle History
            /// </summary>
            public void HandleHistory(int x, int y, Guid historyId, Color previousColor)
            {
                // If the History object exists
                if ((!this.HasHistory) || (History.Id != historyId) && (historyId != Guid.Empty))
                {
                    // here a new History object is created and the pixels are added to it instead
                    this.History = new History(historyId);
                }

                // If the History object exists
                if (this.HasHistory)
                {
                    // Create a new instance of a 'PixelInformation' object.
                    PixelInformation pixel = new PixelInformation();
                    pixel.X = x;
                    pixel.Y = y;
                    pixel.Color = previousColor;

                    // Add this pixel to history
                    this.History.ChangedPixels.Add(pixel);
                }
            }
            #endregion

            #region SetPixel(int x, int y, Color color)
            /// <summary>
            /// method Set Pixel
            /// </summary>
            public void SetPixel(int x, int y, Color color)
            {
                int index = x + (y * Width);
                int col = color.ToArgb();

                Bits[index] = col;
            }
            #endregion

            #region SetPixel(int x, int y, Color color, Guid historyId, Color prevoiusColor)
            /// <summary>
            /// This method Sets a Pixel and it includes a historyId so any changes are stored in history
            /// </summary>
            public void SetPixel(int x, int y, Color color, Guid historyId, Color prevoiusColor)
            {
                // history has to be set before the pixel is set
                // Handle the history
                HandleHistory(x, y, historyId, prevoiusColor);

                int index = x + (y * Width);
                int col = color.ToArgb();

                Bits[index] = col;
            }
            #endregion

            #region UndoChanges()
            /// <summary>
            /// This method Undo Changes
            /// </summary>
            public void UndoChanges()
            {
                // If the History object exists
                if ((this.HasHistory) && (this.History.HasChangedPixels))
                {
                    // get the changed pixels
                    List<PixelInformation> pixels = this.History.ChangedPixels;

                    // Iterate the collection of PixelInformation objects
                    foreach (PixelInformation pixel in pixels)
                    {
                        // for debugging only
                        int alpha = pixel.Color.A;

                        // Restore this pixel 
                        SetPixel(pixel.X, pixel.Y, pixel.Color);
                    }

                    // Remove the history
                    this.History = null;
                }
            }
            #endregion

        #endregion

        #region Properties

            #region Bitmap
            /// <summary>
            /// method [Enter Method Description]
            /// </summary>
            public Bitmap Bitmap { get; private set; }
            #endregion

            #region Bits
            /// <summary>
            /// method [Enter Method Description]
            /// </summary>
            public Int32[] Bits { get; private set; }
            #endregion

            #region BitsHandle
            /// <summary>
            /// This is a ptr to the garbage collector
            /// </summary>
            protected GCHandle BitsHandle { get; private set; }
            #endregion

            #region Disposed
            /// <summary>
            /// method [Enter Method Description]
            /// </summary>
            public bool Disposed { get; private set; }
            #endregion

            #region HasHistory
            /// <summary>
            /// This property returns true if this object has a 'History'.
            /// </summary>
            public bool HasHistory
            {
                get
                {
                    // initial value
                    bool hasHistory = (this.History != null);

                    // return value
                    return hasHistory;
                }
            }
            #endregion

            #region Height
            /// <summary>
            /// method [Enter Method Description]
            /// </summary>
            public int Height { get; private set; }
            #endregion

            #region History
            /// <summary>
            /// This property gets or sets the value for 'History'.
            /// </summary>
            public History History
            {
                get { return history; }
                set { history = value; }
            }
            #endregion

            #region Width
            /// <summary>
            /// method [Enter Method Description]
            /// </summary>
            public int Width { get; private set; }
            #endregion

        #endregion

    }
    #endregion

}

这是完整的项目,因为很难发布完整的应用程序,我试图展示最相关的部分:

https://github.com/DataJuggler/TransparencyMaker

如果有人愿意观看,我的 YouTube 频道上有一个部分供 TransparencyMaker 视频使用:https ://youtu.be/7kfNKyr_oqg

第 2 版已发布,但仍在完善中。

于 2020-02-24T22:53:47.837 回答