贝克曼 DxH800 血球仪图片绘制问题

贝克曼 DxH800 血球仪图片绘制问题

过去开发 LIS 接口对接仪器,大部分血细胞分析仪厂商是希森美康、迈瑞等。

与这些仪器通讯时有个好处,仪器会将图片内容直接发送给我们,或使用第三方软件提前绘制,我们直接读取或对 Base64 数据转码即可,对接都很简单。

但是有些厂商,例如贝克曼、雅培提供的并不是绘制好的图片数据,而是需要我们使用他们提供的数据信息,自行绘制,相对来说开发投入的精力就比希森美康和迈瑞大很多。

以下就是我在绘制血液分析散点图与直方图的一些经验总结。

雅培 Ruby 系列

第一次试水是同事负责的一台雅培 Ruby 系列的全自动血细胞分析仪,图形不是常见的可以读文件或者直接从 Base64 转码。

因为当时在出差,而且不是我负责的,所以没有仔细研究,也仅仅只绘制出了直方图供同事参考。

这里也将这部分代码存在这里,如果以后有用的话备查:

public static Bitmap GetHistgrame(string type, string lines, string base64)
{
    #region 画直线的X坐标
    List<int> listLines = new List<int>();
    string[] arrLines = lines.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries);
    for (int i = 0; i < arrLines.Length; i++)
    {
        int temp = 0;
        if (int.TryParse(arrLines[i], out temp))
        {
            listLines.Add(temp);
        }
    }
    #endregion

    //直方图的X坐标
    List<byte> list = Convert.FromBase64String(base64).ToList();

    Bitmap bitmap = new Bitmap(280, 300);
    using (Graphics graphics = Graphics.FromImage(bitmap))
	using (graphics.FillRectangle(Brushes.White, new Rectangle(0, 0, bitmap.Width, bitmap.Height)))
	using (Pen pen = new Pen(Brushes.Black, 1))
    {
        //画坐标系
        {
            //Y轴
            graphics.DrawLine(pen, 10, 5, 10, 260);// (10,5) (10,260)

            //X轴
            graphics.DrawLine(pen, 10, 260, 265, 260);// (10, 260)(265, 260)

            //X轴上坐标点
            for (int i = 0; i < 6; i++)
            {
                graphics.DrawLine(pen, 10 + i * 50, 260, 10 + i * 50, 262);
                graphics.DrawString((i * 50).ToString(), new Font("宋体", 10), Brushes.Black, i * 50 - 2, 262);
            }
            //描述信息
            graphics.DrawString(type, new Font("宋体", 15), Brushes.Black, 130, 275);
        }

        //画直方图
        {
            pen.Brush = Brushes.Red;
            for (int i = 0; i < list.Count / 2; i++)
            {
                int height = (((int)list[i * 2]) >> 8) + (int)list[i * 2];
                if (height > 0)
                {
                    height = height > 255 ? 255 : height;
                    graphics.DrawLine(pen, 10 + i + 1, 260 - 1, 10 + i + 1, 260 - 1 - height);
                }
            }
        }

        //画线
        {
            pen.Brush = Brushes.Blue;
            for (int i = 0; i < listLines.Count; i++)
            {
                graphics.DrawLine(pen, 10 + listLines[i], 260, 10 + listLines[i], 5);
            }
        }
    }

    return bitmap;
}

调用测试:

string text = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAIAAgADAAMABQAGAAkADQAQABYAGwAcACcALwA3AD8AUQBjAHAAfgCWAKgAswDFAN4A4QDcAOEA7ADsAOoA9AD2AO0A7QD4AP8A+QDwAOwA4ADTAMcAwQCzAJ8AlgCOAIAAcQBmAGIAUQBIAEAANgAtACoAJgAhABoAGgAZABkAGAAYABQAFwAYABoAGgAYABQAEQAVABUAFQAVABQAFAAXABoAHQAbACAAHgAhACgALQAvAC8ANQA7AEcATABSAFcAXgBhAG4AdgB3AHEAeAB/AIEAgACCAIIAfQB/AH8AfAB4AHYAcgBnAGAAWgBWAFEASwA/ADoAMQAxADEALAAmACAAHAAbABUAEwASAAkACQAHAAcABgAFAAMAAwACAAMAAQACAAIAAgACAAIAAgACAAEAAQAAAAAAAAAAAAAAAAABAAEAAQABAAEAAQABAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAQABAAEAAQABAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";

using (Bitmap bitmap = GetHistgrame("WB1", "33\\82", text))
{
    bitmap.Save("WBC.bmp");
}

1

贝克曼 DxH800

开发贝克曼的这台接口时,第一时间想到的就是雅培这个型号的接口,虽然数据结构不太一样,但是直方图绘制思路是类似的。

直方图

仪器那边提供的数据可以解析成 128 个数据点,然后这 128 个数据点对应在 Y轴 的位置,绘制直方图或折线图。

因为雅培的绘制的为直方图,其实也可以绘制成折线图,这里提供一个绘制成折线图的例子:

public static Bitmap GetHistgrame(string type, string hex)
{
    Bitmap result = new Bitmap(720, 320);
    using (Bitmap bitmap = new Bitmap(hex.Length, 256))
    using (Pen pen = new Pen(Brushes.Black, 3))
    {

        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            for (int i = 0; i < hex.Length / 2; i++)
            {
                int num1 = i == 0 ? 0 : int.Parse(hex.Substring((i - 1) * 2, 2), NumberStyles.HexNumber);
                int num2 = int.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber);
                graphics.DrawLine(pen, i * 2 - 1, bitmap.Height - num1, i * 2 + 1, bitmap.Height - num2);
            }
        }

        int left = 20;
        int top = 20;

        using (Font font = new Font(new FontFamily("宋体"), 25, FontStyle.Bold))
        using (Graphics graphics1 = Graphics.FromImage(result))
        {
            //画Y轴
            graphics1.DrawLine(pen, left, top, left, top + 256);

            //画X轴
            graphics1.DrawLine(pen, left, top + 256, left + 640, top + 256);

            //将图形填充
            graphics1.DrawImage(bitmap, new RectangleF(left, top, 640, 256));

            //画刻度
            switch (type.ToUpper())
            {
                case "WBC":
                    {
                        int length = 140;
                        for (int i = 0; i <= 640 / length; i++)
                        {
                            graphics1.DrawLine(pen, left + i * length, top + 256, left + i * length, top + 256 + 5);
                            {
                                graphics1.DrawString((i * 100).ToString(), font, Brushes.Black, left + i * length - 20, top + 256 + 5);
                            }
                        }
                        graphics1.DrawString("IL", font, Brushes.Black, left + 640 - 20, top + 256 + 5);
                    }
                    break;
                case "RBC":
                    {
                        int length = 180;
                        for (int i = 0; i <= 640 / length; i++)
                        {
                            graphics1.DrawLine(pen, left + i * length, top + 256, left + i * length, top + 256 + 5);
                            {
                                graphics1.DrawString((i * 100).ToString(), font, Brushes.Black, left + i * length - 20, top + 256 + 5);
                            }
                        }
                        graphics1.DrawString("IL", font, Brushes.Black, left + 640 - 20, top + 256 + 5);
                    }
                    break;
                case "PLT":
                    {
                        int length = 180;
                        for (int i = 0; i <= 640 / length; i++)
                        {
                            graphics1.DrawLine(pen, left + i * length, top + 256, left + i * length, top + 256 + 5);
                            {
                                graphics1.DrawString((i * 10).ToString(), font, Brushes.Black, left + i * length - 20, top + 256 + 5);
                            }
                        }
                        graphics1.DrawString("IL", font, Brushes.Black, left + 640 - 20, top + 256 + 5);
                    }
                    break;
                default:
                    break;
            }
        }
    }
    return result;
}

调用测试:

string text = "0102020406090F16202D3C4D5E7081909BA4A8AAA9A59E958A7D7064584D443C35302B2824211F1C1A19171616151514141313121212111111111111111111111111111111121212131414151515161617171818191919191919191A1A1A1A1B1C1D1E1E1F2021212222232324242526272828292A2A2B2B2B2C2C2C2C2D2D2D2E2E2E2E2E2E2D2D2D2C2C2B2B2A29282726252423222121201F1E1D1C1B1B1A1A191918181717161514131211100F0E0D0C0C0B0A090908080707070606060505050505040404040303030202020202020101010101010101010100000000000000000000000000000000000000000000000000000000000000000000000000";

using (Bitmap bitmap = GetHistgrame("WBC", text))
{
    bitmap.Save("WBC.bmp");
}

WBC

散点图

其实两次绘图都是卡在散点图的绘制上,不过 DxH800 基本上可以确定是怎么画。

其图片信息简单解析如下:

  • 散点图数据头
    • 传输数据总长度 16 bit MSB+LSB
    • 传输数据块总数 8 bit
  • 散点图数据块
    • 散点图数据块头
      • 散点图类型代码 8 bit

        0x01: 五分类 0x02: 网织红 0x04: 未成熟红细胞

      • 散点图选项代码 8 bit

        0x00: 无渲染信息 0x01: 散点图被压缩 0x02: 有一个有效的渲染信息块

      • 散点图宽度 16 bit MSB+LSB
      • 散点图高度 16 bit MSB+LSB
    • 渲染信息块
      • 渲染信息块头
        • 渲染信息块选项 8 bit

          0x00: 任意选项

        • 调色板表条目数 8 bit
        • 位图图库模式 8 bit
        • 位图像素宽度 8 bit
        • 位图像素高度 8 bit
      • 调色板颜色值 8 bit
      • 抖动显示位图库 8 bit * n

        n = 位图像素宽度 * 位图像素高度

    • 散点图头
      • 散点图数据选项 8 bit

        0x00: 任意选项

      • 散点图基点尺寸 8 bit

        散点图中每个点的大小,通常是 8 表示一个字节

      • 未定义 8 bit
      • 未定义 8 bit
    • 散点图数据
      • 数据点块的数量
      • 数据

按照以上结构解析串口接收到的数据,可以确认散点图是 64 × 64 的图片,问题是由于公司运维提供的数据,其 散点图选项代码0x00 也就是 无渲染信息,所以无法确认通信文档中提到的 调色板 以及 抖动算法

从维基百科中可以了解到,调色板可以使用 8bit 绘制丰富的颜色,而抖动算法可以将图片颜色过度更平滑。

但是这些都涉及到了我的知识盲区,所以我们在不考虑调色板,以及抖动算法,使用红色来绘制这张图看一下效果:

public static Bitmap GetScatter(string type, string hex)
{
    // 计算图片宽高
    int height = int.Parse(hex.Substring(10, 2), NumberStyles.HexNumber)
        + (int.Parse(hex.Substring(12, 2), NumberStyles.HexNumber) << 8);
    int width = int.Parse(hex.Substring(14, 2), NumberStyles.HexNumber)
        + (int.Parse(hex.Substring(16, 2), NumberStyles.HexNumber) << 8);
    
    // 初始化位图
    Bitmap bitmap = new Bitmap(width, height);

    // 从字符串后端截取存储 8bit 调色板颜色的数据
    hex = hex.Substring(hex.Length - height * width * 2);

    // 通过设置位图像素点颜色的方式绘制散点图
    for (int i = 0; i < bitmap.Width; i++)
    {
        for (int j = 0; j < bitmap.Height; j++)
        {
            byte color = byte.Parse(hex.Substring((i * bitmap.Width + j) * 2, 2), NumberStyles.HexNumber);
            bitmap.SetPixel(i, j, Color.FromArgb(color, Color.Red));
        }
    }
    return bitmap;
}

调用一下测试绘制效果:

string text = "0E1001010040004000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F000000000000001500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F00001F00150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F001F00000015000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F1F0000001F00000000001F20001F00000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F211F1F1500000B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020211F1F00000B0B00150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F20001F000B170B160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F2220001F0000161717150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F21200000001517180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022201F000000191B1A1600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021231F0000001A1B190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F1F1F200000151A0B0B0B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0000001F1B1B1A160000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000200000161B0B1A160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F00200000161B1B1A1500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015001F0000181B1B1A160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029002000191B1B18150000000000000000000000000000000000000000000000000000000000000015000029000000000015000000000000000000000000002A00000000181B1B191600000000000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000002929290000151A1B1B170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002A292A150000171B1B1A001500150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002929290000000001161A1A17000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002929292A0000290100191700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000292C002A2901151617001515000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029292E2E2E1500000016150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002D2E2E2D2D00010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000292E2D2E2F2A000000150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002900292C2D2E2F2F2A2A0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000292C2C2F2E2E29000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000292D2E2E2E2E2B2900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002C2D2C2E2D2D2A2900000000000000000000000000000000000000000000000000000000000000000000000000000000000000290000000000000000000000002A2E2E2F2C2E2A0000000000000000000000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000002A2D2E2D2E2E292900000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000002900002C2C2D2B2F2D292A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029002D2D2B2B2E292B0000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002929292A2B2929000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029292A00290000002A171500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029000000000000000029352900000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000293329353334000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000333534003300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000333335003300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003400353434000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033383839393837383400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033000000003637393A3B3B3A3A39363533000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000330000003300373A3A3B3B3B3B3A3A39373300340000000000000000000033000000000000000000000000000000000000000000000000000000000000000000000000003438393A3A3A3A3A3A3937353433000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003300330000330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
using (Bitmap bitmap = GetScatter("5PD1", text))
{
    bitmap.Save(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "5PD1.bmp"));
}

5PD1

使用瑞美动态链接库解决绘图问题

如果不需要出具彩色报告单,以上绘制方法已经满足需求。

以下方案仅供研究学习,如果实际需要应用,建议研究如何开启仪器的渲染信息块传输,自行绘制。

因为瑞美连接的仪器型号比较全面,如果有其他方面的通信问题,也可以参考本方案,从瑞美的通信中学习经验。

显然单调的颜色不符合散点图的绘制需求,网上又检索不到关于血球仪图片绘制的解决方案,那么只有从其他的 LIS 厂商那里学习。

当然第一个能想到,也是最优方案肯定是瑞美,因为只有瑞美可以直接从官网下载到软件的安装包。

我们将瑞美的安装包解压,找到 DxH800 血球仪的接口,反编译解析找到瑞美的解决方案,可以发现,其提供了一个名为 richpic.dll 的动态链接库来绘制图片。

那么接下来就很简单了,我们只需要将文件反解析成瑞美动态链接库需要的格式,同样调用动态链接库解析生成图片。

直方图

首先是直方图,虽然我们成功绘制了图片,但是其提供的解决方案也具有一定参考意义:

/// <summary>
/// 添加直方图
/// </summary>
/// <param name="as_pic">图片数据</param>
/// <param name="as_file">存储文件路径</param>
/// <param name="as_picname">项目名称</param>
/// <param name="xxs">直方图相对 X轴 缩放</param>
/// <param name="yxs">直方图相对 Y轴 缩放</param>
/// <param name="pwidht">图片宽度</param>
/// <param name="pheight">图片高度</param>
/// <param name="xlen">X轴 长度</param>
/// <param name="ylen">Y轴 长度</param>
/// <param name="xinc">X轴 坐标点间距</param>
/// <param name="linew">画图线宽</param>
/// <param name="dotlen">未知 一般设置为3</param>
/// <returns></returns>
[DllImport("richpic.dll")]
public static extern int func_creathistogram(string as_pic, string as_file, string as_picname, double xxs, double yxs, int pwidht, int pheight, int xlen, int ylen, int xinc, int linew, int dotlen);

private static int AddGraphHistogram(DateTime sampleDate, string sampleNo, string graphItem, string graphData, int seq, string fileExt, ref string graphFile)
{
    try
    {
        graphFile = $@"{AppDomain.CurrentDomain.BaseDirectory}\{sampleDate:yyyyMM}\{sampleDate:yyyyMMdd}_{sampleNo}_{graphItem}.{fileExt}";

        if (!Directory.Exists(Path.GetDirectoryName(graphFile)))
            Directory.CreateDirectory(Path.GetDirectoryName(graphFile));

        double xxs = 2d;
        double yxs = 1d;
        int picw = 300;
        int pich = 180;
        int xlen = 150;
        int ylen = 200;
        int xinc = 50;
        int linew = 1;
        int dotlen = 3;
        return func_creathistogram(graphData, graphFile, graphItem, xxs, yxs, picw, pich, xlen, ylen, xinc, linew, dotlen);
    }
    catch (Exception exc)
    {
        Console.WriteLine(exc.Message);
        return -1;
    }
}

调用需要传输一些图片设置的信息,包括图片大小、X轴、Y轴、缩放、线宽、图片后缀名等。

测试一下瑞美画图调用效果,动态链接库会将图片写入到我们指定的路径:

string text = "0102020406090F16202D3C4D5E7081909BA4A8AAA9A59E958A7D7064584D443C35302B2824211F1C1A19171616151514141313121212111111111111111111111111111111121212131414151515161617171818191919191919191A1A1A1A1B1C1D1E1E1F2021212222232324242526272828292A2A2B2B2B2C2C2C2C2D2D2D2E2E2E2E2E2E2D2D2D2C2C2B2B2A29282726252423222121201F1E1D1C1B1B1A1A191918181717161514131211100F0E0D0C0C0B0A090908080707070606060505050505040404040303030202020202020101010101010101010100000000000000000000000000000000000000000000000000000000000000000000000000";

// 瑞美动态链接库解析需要将数据由十六进制转换成十进制
StringBuilder strGragh = new StringBuilder();
for (int i = 0; i < text.Length; i += 2)
{
    strGragh.Append(int.Parse(text.Substring(i, 2), NumberStyles.HexNumber).ToString("000"));
}

// 封装了一个方法 可以传入标本日期标本号等 解析完成后返回该图片所在路径
string path = "";
int result = AddGraphHistogram(DateTime.Now, "1", "WBC", strGragh.ToString(), 1, "gif", ref path);


if (result != -1)
{
    Console.WriteLine($"转换成功:{path}");
}
else
{
    Console.WriteLine("转换失败");
}

20191219_1_WBC

注意:瑞美图片的绘制信息是可以通过配置文件设置的,如果需要调整坐标轴的信息达到和仪器中图片效果一致,我们也需要设置这些参数,该测试没有根据实际需要的图片坐标信息对入参调整,而是将入参固定成一个值测试绘制效果。

散点图

同样的散点图也调用动态链接库进行绘制:

/// <summary>
/// 添加散点图
/// </summary>
/// <param name="as_picstr">图片数据</param>
/// <param name="as_line">未知</param>
/// <param name="colortype">颜色类型,可能和调色板有关</param>
/// <param name="xlen">X轴 长度</param>
/// <param name="ylen">Y轴 长度</param>
/// <param name="filename">存储文件路径</param>
/// <returns></returns>
[DllImport("richpic.dll")]
public static extern int func_creatscatter(string as_picstr, string as_line, int colortype, int xlen, int ylen, string filename);

private static int AddGraphScatter(DateTime sampleDate, string sampleNo, string graphItem, string graphData, string graphLine, int seq, string fileExt, ref string graphFile)
{
    graphFile = $@"{AppDomain.CurrentDomain.BaseDirectory}\{sampleDate:yyyyMM}\{sampleDate:yyyyMMdd}_{sampleNo}_{graphItem}.{fileExt}";

    if (!Directory.Exists(Path.GetDirectoryName(graphFile)))
        Directory.CreateDirectory(Path.GetDirectoryName(graphFile));

    int xlen = 64; // 实际是图片宽高
    int ylen = 64;
    int color = 3;
    return func_creatscatter(graphData, graphLine, color, xlen, ylen, graphFile);
}

调用这个方法可以将图片绘制到指定目录:

string text = "0E1001010040004000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F000000000000001500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F00001F00150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F001F00000015000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F1F0000001F00000000001F20001F00000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F211F1F1500000B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020211F1F00000B0B00150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F20001F000B170B160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F2220001F0000161717150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F21200000001517180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022201F000000191B1A1600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021231F0000001A1B190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F1F1F200000151A0B0B0B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0000001F1B1B1A160000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000200000161B0B1A160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F00200000161B1B1A1500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015001F0000181B1B1A160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029002000191B1B18150000000000000000000000000000000000000000000000000000000000000015000029000000000015000000000000000000000000002A00000000181B1B191600000000000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000002929290000151A1B1B170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002A292A150000171B1B1A001500150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002929290000000001161A1A17000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002929292A0000290100191700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000292C002A2901151617001515000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029292E2E2E1500000016150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002D2E2E2D2D00010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000292E2D2E2F2A000000150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002900292C2D2E2F2F2A2A0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000292C2C2F2E2E29000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000292D2E2E2E2E2B2900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002C2D2C2E2D2D2A2900000000000000000000000000000000000000000000000000000000000000000000000000000000000000290000000000000000000000002A2E2E2F2C2E2A0000000000000000000000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000002A2D2E2D2E2E292900000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000002900002C2C2D2B2F2D292A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029002D2D2B2B2E292B0000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002929292A2B2929000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029292A00290000002A171500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029000000000000000029352900000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000293329353334000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000333534003300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000333335003300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003400353434000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033383839393837383400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033000000003637393A3B3B3A3A39363533000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000330000003300373A3A3B3B3B3B3A3A39373300340000000000000000000033000000000000000000000000000000000000000000000000000000000000000000000000003438393A3A3A3A3A3A3937353433000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003300330000330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";

// 通过图片宽高计算用于绘制图形内容的数据
int height = int.Parse(text.Substring(10, 2), NumberStyles.HexNumber)
    + (int.Parse(text.Substring(12, 2), NumberStyles.HexNumber) << 8);
int width = int.Parse(text.Substring(14, 2), NumberStyles.HexNumber)
    + (int.Parse(text.Substring(16, 2), NumberStyles.HexNumber) << 8);
string resultText = text.Substring(text.Length - height * width * 2);

// 瑞美动态链接库解析需要提供坐标点 以及对应坐标点的十进制调色板颜色值
StringBuilder strDec = new StringBuilder();
for (int i = 0; i < resultText.Length; i += 2)
{
    strDec.Append($"{((i + 1) / 128)},{((i / 2) % 64)},{int.Parse(resultText.Substring(i, 2), System.Globalization.NumberStyles.HexNumber).ToString("000")}|");
}

// 封装了一个方法 可以传入标本日期标本号等 解析完成后返回该图片所在路径
string path = "";
int result = AddGraphScatter(DateTime.Now, "1", "5PD1", strDec.ToString(), "", 4, "gif", ref path);

if (result != -1)
{
    Console.WriteLine($"转换成功:{path}");
}
else
{
    Console.WriteLine("转换失败");
}

20191219_1_5PD1

这时可以对比瑞美和我们前面绘制的图片,除了因为调色板的问题颜色不对外,其他的信息基本一致。当然瑞美动态链接库绘制的图片也没有使用所谓的抖动算法对图形进行优化就是了。

20191219_1_5PD1

注:文中部分图片绘制较小,可以缩放查看。

参考:

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×