【问题标题】:CSV File to BitmapCSV 文件到位图
【发布时间】:2018-03-20 05:15:07
【问题描述】:

我有一个充满数据的大型 CSV 文件,行中的前 3 个值是不必要的,其余的填充了一行的值,以及许多代表图像的行。 Example X,X,X,1,2,3,4,5 X,X,X,6,7,8,9,10 X,X,X,11,12,13,14,15

我正在尝试将其重建为灰度位图,我可以在图片框中显示并导出(只要有很多其他调整,我不会在这里进行,因为这不是问题)

过去 2 天我一直在努力解决这个问题。我试图直接将 CSV 解析为 Image.SetPixel,但我遇到了无数问题。我尝试将 CSV 加载到 2D 数组中,但在获取数组的长度时遇到了问题,并且问题超出了界限,以至于我放弃了这两种尝试,因为试图纠正问题导致代码混乱。

有没有人有解决此类问题的经验?以前做过吗?有什么建议或代码sn-ps吗?非常感谢,不用说我不是最擅长的。

【问题讨论】:

  • 最好将示例 csv 附加到问题中。
  • 文件很大,找不到上传文件的地方
  • 这个例子已经足够IMO了。它显示了数据的格式,可以实际上用作 5x3 图像。
  • 我尝试将 CSV 直接解析为 Image.SetPixel,但遇到了无数问题。 那可能是什么问题?这是第一道攻击线,有点慢,但可以得到检查数据的结果。稍后您可以转到 Lockbits 以提高速度。当然,您必须正确解析并知道这些数字的含义!
  • @TaW 是的,缺少任何代码有点不确定,但无论如何我都会用 8 位来做到这一点,因此我已经回答了。虽然我能看到的唯一明显问题是某些行是否不完整......否则第一行的分割结果应该给出图像宽度,而行数显然是高度......

标签: c# arrays csv bitmap


【解决方案1】:

这看起来与 8 位图像有关。 8 位图像的优点是您只需要一个包含您的值的一维字节数组,and you can pretty much load that straight into an image, using LockBits and Marshal.Copy.

嗯,几乎是直的。 8 位图像上的像素值实际上并不是您的颜色。它们是对调色板的引用,实际上包含您的颜色。但是如果您希望图像上的值 0 到 255 指代颜色 (0,0,0) 到 (255,255,255),那么您需要做的就是生成一个包含您想要的 256 种灰色颜色的调色板,可以处理在一个简单的for-loop 中。

但首先是 CSV。如果这真的只是简单的“数字、逗号、数字”信息,您可以简单地使用 String.Split,但对于任何更高级/可靠的 CSV 解析,可以处理包含引号和/或拆分字符的引用块的特殊情况,你需要TextFieldParser。有关这方面的更多信息,请参阅 here,尽管我认为为此我们可以选择 String.Split 解决方案。

为方便起见,我在此处设置了 startColumn 变量,但在您的情况下,它当然是“3”。

public static Bitmap GrayImageFromCsv(String[] lines, Int32 startColumn, Int32 maxValue)
{
    // maxValue cannot exceed 255
    maxValue = Math.Min(maxValue, 255);
    // Read lines; this gives us the data, and the height.
    //String[] lines = File.ReadAllLines(path);
    if (lines == null || lines.Length == 0)
        return null;
    Int32 bottom = lines.Length;
    // Trim any empty lines from the start and end.
    while (bottom > 0 && lines[bottom - 1].Trim().Length == 0)
        bottom--;
    if (bottom == 0)
        return null;
    Int32 top = 0;
    while (top < bottom && lines[top].Trim().Length == 0)
        top++;
    Int32 height = bottom - top;
    // This removes the top-bottom stuff; the new array is compact.
    String[][] values = new String[height][];
    for (Int32 i = top; i < bottom; i++)
        values[i - top] = lines[i].Split(',');
    // Find width: maximum csv line length minus the amount of columns to skip.
    Int32 width = values.Max(line => line.Length) - startColumn;
    if (width <= 0)
        return null;
    // Create the array. Since it's 8-bit, this is one byte per pixel.
    Byte[] imageArray = new Byte[width*height];
    // Parse all values into the array
    // Y = lines, X = csv values
    for (Int32 y = 0; y < height; y++)
    {
        Int32 offset = y*width;
        // Skip indices before "startColumn". Target offset starts from the start of the line anyway.
        for (Int32 x = startColumn; x < values[y].Length; x++)
        {
            Int32 val;
            // Don't know if Trim is needed here. Depends on the file.
            if (Int32.TryParse(values[y][x].Trim(), out val))
                imageArray[offset] = (Byte) Math.Max(0, Math.Min(val, maxValue));
            offset++;
        }
    }
    // generate gray palette for the given range, by calculating the factor to multiply by.
    Double mulFactor = 255d / maxValue;
    Color[] palette = new Color[maxValue + 1];
    for (Int32 i = 0; i <= maxValue; i++)
    {
        // Away from zero rounding: 2.4 => 2 ; 2.5 => 3
        Byte g = (Byte)Math.Round(i * mulFactor, MidpointRounding.AwayFromZero);
        palette[i] = Color.FromArgb(g, g, g);
    }
    // Since the palette is incomplete, give the color fill arg as Color.White
    return BuildImage(imageArray, width, height, width, PixelFormat.Format8bppIndexed, palette, Color.White);
}

称为:

String[] lines = File.ReadAllLines(path);
using (Bitmap img = GrayImageFromCsv(lines, 3, 15))
{
    // null = conversion failed. Could log/show warning.
    if (img != null)
        img.Save("fromcsv.png", ImageFormat.Png);
}

在主处理结束时调用的 BuildImage 函数执行上述“将字节数组加载到图像中”操作。 It can be found here. 请注意,“步幅”是图像一行上的字节数。虽然这与 8 位图像的宽度相同,但由于每个字节是一个像素,因此对于其他格式会有所不同。

【讨论】:

  • 首先感谢您的回复,我相信它会很有帮助,但是尝试一下似乎不知道如何处理您返回的“ImageUtils”,这是图书馆的一部分?
  • 在网上看它似乎只需要'System.Drawing'和'System.Drawing.Imaging'这两个我很困惑为什么它说它在当前上下文中不存在跨度>
  • @user3599976 阅读最后一段。我链接到该功能(我现在对其进行了一些编辑以使链接更加明显)。在我的个人代码库中,它位于我的静态工具类 ImageUtils 中,但显然您可以将它放在任何您想要的位置并更改/删除 ImageUtils 部分。
  • 非常感谢,经过修改以适应我的数据范围并添加手动偏移量和乘数,效果非常好!
  • 您知道,您实际上可以在调色板上进行乘法运算。例如,如果您有 0 到 50 的范围,那么您可以将数据保留为仅 0-50,而是确保 0-50 调色板索引是全黑到白的淡入淡出,只需采取更大的步骤当用灰度值填充调色板时。然后你只需要处理这些,而不是你收到的每一条数据。它的另一个优势是您的原始数据 100% 保留在图像中。
猜你喜欢
  • 1970-01-01
  • 2013-12-16
  • 1970-01-01
  • 2012-07-28
  • 2012-07-08
  • 1970-01-01
  • 1970-01-01
  • 2020-12-16
  • 2013-03-16
相关资源
最近更新 更多