首页 > 其他分享 >Bmp读写二值图

Bmp读写二值图

时间:2024-07-08 09:31:07浏览次数:19  
标签:stream int 读写 Write Bmp br 二值 byte writeStream

public class Bmp : IDisposable
{
    /*
        * 每行像素数据必须是4的倍数
        * 黑白二值图0表示黑,1表示白
        */
    public int Width { get => _width; }
    public int Height { get => Math.Abs(_height); }
    public int BitCount { get => _bitCount; }
    public int XResolution { get => _xresolution > 0 ? _xresolution : 96; }
    public int YResolution { get => _yresolution > 0 ? _yresolution : 96; }

    public void Dispose()
    {
        _br?.Dispose();
        _readStream?.Dispose();
        _writeStream?.Dispose();
    }

    Stream _readStream;
    BinaryReader _br;
    MemoryStream _writeStream;
    public Bmp(string path): this(File.OpenRead(path))
    {
    }

    public Bmp(int width, int height, int bitCount)
    {
        _width = width;
        _height = height;
        _bitCount = (short)bitCount;
        LoadStride();
        _imageSize = _stride * Math.Abs(_height);
        ResetWriteStream();
    }

    public Bmp(Stream stream)
    {
        _readStream = stream;
        _br = new BinaryReader(_readStream);

        char[] bm = _br.ReadChars(2);
        if (bm[0] != 'B' || bm[1] != 'M') throw new InvalidDataException();
        _fileSize = _br.ReadInt32();
        _reserved1 = _br.ReadInt16();
        _reserved2 = _br.ReadInt16();
        _offBits = _br.ReadInt32();
        _headSize = _br.ReadInt32();
        _width = _br.ReadInt32();
        _height = _br.ReadInt32();
        _planes = _br.ReadInt16();
        _bitCount = _br.ReadInt16();
        _compressionType = _br.ReadInt32();
        _imageSize = _br.ReadInt32();
        _xresolution = _br.ReadInt32();
        _yresolution = _br.ReadInt32();
        _clrUsed = _br.ReadInt32();
        _clrImportant = _br.ReadInt32();
        LoadStride();
        ReadStreamToWriteStream();
    }

    private void ReadStreamToWriteStream()
    {
        if (_writeStream != null)
            _writeStream.Dispose();
        _writeStream = new MemoryStream();
        byte[] bs = new byte[1024];
        _writeStream.Seek(0, SeekOrigin.Begin);
        _readStream.Seek(_offBits, SeekOrigin.Begin);
        CopyStream(_readStream, _writeStream);
    }

    int _fileSize = 0;
    /// <summary>
    /// 必须是0
    /// </summary>
    short _reserved1 = 0;
    /// <summary>
    /// 必须是0
    /// </summary>
    short _reserved2 = 0;
    /// <summary>
    /// 像素数据开始的字节偏移量
    /// </summary>
    int _offBits = 0;
    /// <summary>
    /// 图片头数据长度
    /// </summary>
    int _headSize = 0;
    int _width = 0;
    /// <summary>
    /// 正数时像素字节流流从左下角开始右上角结束,负数时左上角开始,右下角结束,通常是正数
    /// </summary>
    int _height = 0;
    /// <summary>
    /// 平面数,总是等于1
    /// </summary>
    short _planes = 1;
    /// <summary>
    /// 色宽[1,4,8,16,24,32]
    /// </summary>
    short _bitCount = 0;
    /// <summary>
    /// 压缩,0表示无压缩,1表示8位压缩(只用于8色宽),2表示4位压缩(只用于4色宽),3表示(只用于16、32色宽),4表示jpeg(只用于打印机),5表示png(只用于打印机)
    /// </summary>
    int _compressionType = 0;
    /// <summary>
    /// 图片像素数据区长度未压缩时可以是0
    /// </summary>
    int _imageSize = 0;
    /// <summary>
    /// pixels per meter
    /// </summary>
    int _xresolution = 0;
    /// <summary>
    /// pixels per meter
    /// </summary>
    int _yresolution = 0;
    /// <summary>
    /// 使用的调色板颜色索引数量,0表示所有;大于色宽等于24位则没有颜色索引
    /// </summary>
    int _clrUsed = 0;
    /// <summary>
    /// 对图像显示有重要影响的颜色索引数量,0表示所有
    /// </summary>
    int _clrImportant = 0;

    int _stride = 0;
    public int Stride
    {
        get
        {
            if(_stride == 0)
                LoadStride();
            return _stride;
        }
    }

    void LoadStride()
    {
        _stride = Math.Max(_width * _bitCount * 4 / 32, 4);
    }

    public int GetOffset(int x, int y, bool containsHead = false)
    {
        int offsetx = (int)Math.Floor(x / 8d);
        int offset = offsetx + Stride * (_height > 0 ? _height - 1 - y : y);
        return (containsHead ? _offBits : 0) + offset;
    }

    public Color GetPixel(int x, int y)
    {
        switch (_bitCount)
        {
            case 1:
                {
                    byte[] bs = ReadBytes(x, y);
                    int p = bs[0] & (int)Math.Pow(2, 7 - (x % 8));
                    return p == 0 ? Color.Black : Color.White;
                }
            default:
                break;
        }
        return Color.Empty;
    }

    byte[] ReadBytes(int x, int y)
    {
        switch (_bitCount)
        {
            case 1:
                {
                    _writeStream.Seek(GetOffset(x, y), SeekOrigin.Begin);
                    return new byte[] { (byte)_writeStream.ReadByte() };
                }
            default:
                break;
        }
        return new byte[0];
    }

    public void SetPixel(int x, int y, Color color)
    {
        ValidWriteStream(true);
        switch (_bitCount)
        {
            case 1:
                {
                    byte[] bs = ReadBytes(x, y);
                    _writeStream.Seek(GetOffset(x, y), SeekOrigin.Begin);
                    int v = color.ToArgb() == Color.White.ToArgb() ? 1 : 0;
                    int pow = (int)Math.Pow(2, 7 - (x % 8));
                    if ((bs[0] & pow) == v) return;
                    if(v == 1)
                        _writeStream.WriteByte((byte)(bs[0] | pow));
                    else
                        _writeStream.WriteByte((byte)(bs[0] - pow));
                    return;
                }
            default:
                break;
        }
    }

    public void Save(Stream stream)
    {
        int fileHeadSize = 14;
        _headSize = 40;
        int colorSize = 0;//颜色索引字节长度
        switch (_bitCount)
        {
            case 1:
                {
                    colorSize = 8;
                    break;
                }
            default:
                break;
        }
        _offBits = fileHeadSize + _headSize + colorSize;
        int fileSize = _offBits + _imageSize;

        //file head; length:14
        stream.Write(new byte[] { (byte)'B', (byte)'M' }, 0, 2);
        stream.Write(BitConverter.GetBytes(fileSize), 0, 4);
        stream.Write(BitConverter.GetBytes(_reserved1), 0, 2);
        stream.Write(BitConverter.GetBytes(_reserved2), 0, 2);
        stream.Write(BitConverter.GetBytes(_offBits), 0, 4);

        //image head; length:40
        stream.Write(BitConverter.GetBytes(_headSize), 0, 4);
        stream.Write(BitConverter.GetBytes(_width), 0, 4);
        stream.Write(BitConverter.GetBytes(_height), 0, 4);
        stream.Write(BitConverter.GetBytes(_planes), 0, 2);
        stream.Write(BitConverter.GetBytes(_bitCount), 0, 2);
        stream.Write(BitConverter.GetBytes(_compressionType), 0, 4);
        stream.Write(BitConverter.GetBytes(_imageSize), 0, 4);
        stream.Write(BitConverter.GetBytes(_xresolution), 0, 4);
        stream.Write(BitConverter.GetBytes(_yresolution), 0, 4);
        stream.Write(BitConverter.GetBytes(_clrUsed), 0, 4);
        stream.Write(BitConverter.GetBytes(_clrImportant), 0, 4);
            
        //color used
        switch (_bitCount)
        {
            case 1:
                {
                    stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);
                    stream.Write(new byte[] { 255, 255, 255, 0 }, 0, 4);
                    break;
                }
            default:
                break;
        }

        //body
        _writeStream.Seek(0, SeekOrigin.Begin);
        CopyStream(_writeStream, stream);
    }

    void CopyStream(Stream source, Stream target)
    {
        byte[] bs = new byte[1024];
        int len;
        do
        {
            len = source.Read(bs, 0, bs.Length);
            if (len > 0)
                target.Write(bs, 0, len);
        } while (len > 0);
    }

    bool ValidWriteStream(bool resetIfInvalid)
    {
        bool valid = true;
        if (_writeStream == null || !_writeStream.CanWrite)
            valid = false;

        if (!valid && resetIfInvalid)
            ResetWriteStream();
        return valid;
    }

    void ResetWriteStream()
    {
        byte[] bs = new byte[_imageSize];
        if (_readStream != null)
            _readStream.Read(bs, 0, bs.Length);
        _writeStream = new MemoryStream(bs);
    }
}

使用参考:

using(Bmp bmp = new Bmp(@"C:\Users\admin\Pictures\5.bmp"))
{
    Color color = bmp.GetPixel(10, 10);
    Color color1 = bmp.GetPixel(444, 0);
    ;
}
using (Bmp bmp = new Bmp(10, 10, 1))
{
    for (int i = 0; i < bmp.Width; i++)
    {
        for (int j = 0; j < bmp.Height; j+=2)
            bmp.SetPixel(i, j, Color.White);
    }
    using(FileStream fs = File.OpenWrite("C:\\Users\\admin\\Pictures\\4.bmp"))
        bmp.Save(fs);
}

 

标签:stream,int,读写,Write,Bmp,br,二值,byte,writeStream
From: https://www.cnblogs.com/RedSky/p/18287533

相关文章

  • cv2中二值图轮廓与轮廓层级参数cv2.RETR_TREE
    1.二值图的轮廓在使用cv2.findContours时,黑白二值图(像素值只有0或255)的轮廓都是以白色像素作为前景,黑色像素作为背景。看下面两个图(左图与右图同样大小都是200x200,左图是四周为黑色,中间为白色,右图为四周为白色,中间为黑色)。               ......
  • SQLSugar 基本语法+数据库读写分离
    面向对象的操作数据库,相比EFCore、Dapper等其他ORM框架性能支持性能轻便快捷,数据库的读写分离能大大减轻数据库的压力一、NuGet下载安装SqlSugarCore二、实例化SqlSugarCore---包含数据库链接---指定数据库类型---增删改查,上代码这里演示使用控制台程序usingSqlSugar;......
  • BMP(Bitmap Image File)解码流程:
    BMP(BitmapImageFile)的解码流程主要包括以下几个步骤,这些步骤确保了BMP图像文件能够被正确地读取并显示在屏幕上:1.读取文件头信息目的:确认文件类型和基本信息。内容:BMP文件以特定的文件头标识开始,这包括文件的类型(必须是“BM”,以十六进制表示为0x4D42)、文件大小、保留字(......
  • 并发编程之读写锁
    什么是读写锁?读写读非阻塞阻塞写阻塞阻塞如果读操作频率远高于写操作,那读写锁就能大大提升系统性能看两组对比例子:先定义一个线程池,2000核心线程,4000最大线程,当所有线程执行结束之后,计算耗时publicclassTestPoolExecutorextendsThreadPoolExecutor{private......
  • 存储读写之FLASH篇2-本篇内容来自野火文档
    STM32的内部FLASH简介在STM32芯片内部,存在一个重要的FLASH存储器,其主要用途是存储应用程序代码。编写完应用程序后,通常需要使用下载工具将已编译的代码文件写入内部FLASH。不可忽视的是,内部FLASH具有非易失性存储的特性,这意味着在断电后存储的数据不会丢失。每次芯片重新上......
  • 达梦数据库系列—20. 读写分离集群搭建
    目录一、配置读写分离集群1、环境说明2、数据准备3、配置主库GRP1_RWW_01配置dm.ini配置dmmal.ini配置dmarch.ini配置dmwatcher.ini启动主库设置OGUID修改数据库模式4、配置备库GRP1_RWW_02配置dm.ini配置dmmal.ini配置dmarch.ini配置dmwatcher.in......
  • 如何使用C++进行文件读写操作
    在C++中,我们可以使用标准库中的 <fstream>(文件流)来进行文件的读写操作。以下是一些基本的文件读写操作的示例。读取文件cpp复制代码#include<fstream>#include<iostream>#include<string>intmain(){std::ifstreamfile("example.txt");//打开文件以进行读取操......
  • MySQL主从复制与读写分离
    一、MySQL主从复制概述1.MySQL主从复制原理MySQL的主从复制和读写分离紧密相连,首先部署主从复制,才能在此基础上进行读写分离。2.MySQL支持的复制类型基于语句的复制:在主服务器上执行的语句,在从服务器上执行同样语句。MySQL默认采用该语句,效率较高。基于行的复制:把改变的......
  • Flash均衡读写
    #defineFLASH_INITIAL_BYTE0xff #definePAGE_NUM_PER_CONFIG       2 #defineCONFIG_FLASH_PAGE_START     508#defineCONFIG_BUF_SIZEsizeof(CFG_CHARGE_ST)/2#define  CONFIG_FLASH_ADDRESS_START......
  • 【小沐学GIS】Google的kml文件的读写(C++、Python)
    文章目录1、简介1.1kml简介1.2功能点1.2.1地标1.2.2地面叠加层1.2.3路径1.2.4多边形2、下载和编译3、C++测试4、Python测试4.1安装库4.2测试14.2测试24.3测试3结语1、简介https://developers.google.cn/kml/documentation/kmzarchives?hl=zh-cn1.1kml......