C# 使用10位深度的数字图像
在像素深度为10位的情况下,如何使用c#(读取每个像素的值)处理和提取图像数据 此外,图像有4个波段(R、G、B和NIR)C# 使用10位深度的数字图像,c#,image-processing,C#,Image Processing,在像素深度为10位的情况下,如何使用c#(读取每个像素的值)处理和提取图像数据 此外,图像有4个波段(R、G、B和NIR) 谢谢。 < P> C++中的代码不在C中,所以你需要输入我的代码…< /P> 您应该添加像素组成(每个频带的位数及其顺序) 你写了10位每像素和R,G,B,NIR(我假设近红外)波段 您未指定像素格式 因此,我将创建一个,您必须将其更改为您的案例 bit: |9 8 7 6 5 4 3 2 1 0| band: | R | G | B | NIR | R-2位
谢谢。
< P> C++中的代码不在C中,所以你需要输入我的代码…< /P> 您应该添加像素组成(每个频带的位数及其顺序)- 你写了10位每像素和R,G,B,NIR(我假设近红外)波段
- 您未指定像素格式
- 因此,我将创建一个,您必须将其更改为您的案例强>
bit: |9 8 7 6 5 4 3 2 1 0| band: | R | G | B | NIR |
- R-2位
- G-3位
- B-2位
- 近红外-3位
- 我会将其转换为更易于管理的位大小(例如,每个频带4位)
- 所以我可以使用标准数据类型
- 完成处理后,只需将其转换回10位像素格式
- 假设这符合你的形象
int xs,ys; // resolution int siz; // BYTE size of whole image data ... siz = ceil(xs*ys*10/8) BYTE *dat=new BYTE[siz+5]; // 10bit image data
- 那么现在如何从5个字节中读取4个像素,并将其转换为更多字节对齐的内容
- 数据布局如下所示:
| 0 | 1 | 2 | 3 | 4 | // BYTE |rrgggbbn|nn rrgggb|bnnn rrgg|gbbnnn rr|gggbbnnn| | 0 | 1 | 2 | 3 | // Pixel
void convert10to16 (BYTE *dst,BYTE *src) { int i=0,o=0; BYTE in,out; in=scr[i]; i++; // rrgggbbn out =(in>>2)&0x30; // 00rr0000 out|=(in>>3)&0x07; // 00rr0ggg dst[o]=out; o++; out =(in<<3)&0x30; // 00bb0000 out|=(in<<2)&0x04; // 00bb0n00 in=scr[i]; i++; // nnrrgggb out|=(in>>6)&0x03; // 00bb0nnn dst[o]=out; o++; out =(in )&0x30; // 00rr0000 out|=(in>>1)&0x07; // 00rr0ggg dst[o]=out; o++; out =(in<<5)&0x20; // 00b00000 in=scr[i]; i++; // bnnnrrgg out|=(in>>3)&0x10; // 00bb0000 out|=(in>>4)&0x07; // 00bb0nnn dst[o]=out; o++; out =(in<<2)&0x30; // 00rr0000 out|=(in<<1)&0x06; // 00rr0gg0 in=scr[i]; i++; // gbbnnnrr out|=(in>>7)&0x01; // 00rr0ggg dst[o]=out; o++; out =(in>>1)&0x30; // 00bb0000 out|=(in>>2)&0x07; // 00bb0nnn dst[o]=out; o++; out =(in<<4)&0x30; // 00rr0000 in=scr[i]; i++; // gggbbnnn out|=(in>>5)&0x07; // 00rr0ggg dst[o]=out; o++; out =(in<<1)&0x30; // 00bb0000 out|=(in )&0x07; // 00bb0nnn dst[o]=out; o++; }
- 首先选择与字节对齐的新像素格式
bit: |15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0| band: | R | G | B | NIR | // all bands are 4 bits
- 我会像这样转换像素格式:
| 0 | 1 | 2 | 3 | 4 | // BYTE |rrgggbbn|nn rrgggb|bnnn rrgg|gbbnnn rr|gggbbnnn| | 0 | 1 | 2 | 3 | // Pixel
void convert10to16 (BYTE *dst,BYTE *src) { int i=0,o=0; BYTE in,out; in=scr[i]; i++; // rrgggbbn out =(in>>2)&0x30; // 00rr0000 out|=(in>>3)&0x07; // 00rr0ggg dst[o]=out; o++; out =(in<<3)&0x30; // 00bb0000 out|=(in<<2)&0x04; // 00bb0n00 in=scr[i]; i++; // nnrrgggb out|=(in>>6)&0x03; // 00bb0nnn dst[o]=out; o++; out =(in )&0x30; // 00rr0000 out|=(in>>1)&0x07; // 00rr0ggg dst[o]=out; o++; out =(in<<5)&0x20; // 00b00000 in=scr[i]; i++; // bnnnrrgg out|=(in>>3)&0x10; // 00bb0000 out|=(in>>4)&0x07; // 00bb0nnn dst[o]=out; o++; out =(in<<2)&0x30; // 00rr0000 out|=(in<<1)&0x06; // 00rr0gg0 in=scr[i]; i++; // gbbnnnrr out|=(in>>7)&0x01; // 00rr0ggg dst[o]=out; o++; out =(in>>1)&0x30; // 00bb0000 out|=(in>>2)&0x07; // 00bb0nnn dst[o]=out; o++; out =(in<<4)&0x30; // 00rr0000 in=scr[i]; i++; // gggbbnnn out|=(in>>5)&0x07; // 00rr0ggg dst[o]=out; o++; out =(in<<1)&0x30; // 00bb0000 out|=(in )&0x07; // 00bb0nnn dst[o]=out; o++; }
void convert10to16(字节*dst,字节*src) { int i=0,o=0; 字节输入,字节输出; in=scr[i];i++;//rrgggbbn out=(in>>2)&0x30;//00rr0000 out |=(in>>3)&0x07;//00rr0ggg dst[o]=out;o++; out=(in1)&0x07;//00rr0ggg dst[o]=out;o++; out=(in3)&0x10;//00bb0000 out |=(in>>4)&0x07;//00bb0nnn dst[o]=out;o++; out=(in1)&0x30;//00bb0000 out |=(in>>2)&0x07;//00bb0nnn dst[o]=out;o++; out=(in5)&0x07;//00rr0ggg dst[o]=out;o++; out=(in我从的代码中得到启发,编写了这个类,根据数组中每个颜色分量的位长度和位位置自动转换颜色:
不过,现在获取颜色值的实际方法比我在示例解码注释中使用的简单*8更为精确;/// <summary> /// Class to automate the unpacking (and packing/writing) of RGB(A) colours in colour formats with packed bits. /// Inspired by https://github.com/scummvm/scummvm/blob/master/graphics/pixelformat.h /// This class works slightly differently than the ScummVM version, using 4-entry arrays for all data, with each entry /// representing one of the colour components, so the code can easily loop over them and perform the same action on each one. /// </summary> public class PixelFormatter { /// <summary>Standard PixelFormatter for .Net's RGBA format.</summary> public static PixelFormatter Format32BitArgb = new PixelFormatter(4, 8, 16, 8, 8, 8, 0, 8, 24, true); /// <summary>Number of bytes to read per pixel.</summary> private Byte bytesPerPixel; /// <summary>Amount of bits for each component (R,G,B,A)</summary> private Byte[] bitsAmounts = new Byte[4]; /// <summary>Amount of bits to shift for each component (R,G,B,A)</summary> private Byte[] shiftAmounts = new Byte[4]; /// <summary>Masks to limit the amount of bits for each component, derived from the bitsAmounts.</summary> private UInt32[] limitMasks = new UInt32[4]; /// <summary>Multiplier for each component (R,G,B,A). If not explicitly given this can be derived from the number of bits.</summary> private Double[] multipliers = new Double[4]; /// <summary>Defaults for for each component (R,G,B,A)</summary> private Byte[] defaults = new Byte[] { 0, 0, 0, 255 }; /// <summary>True to read the input bytes as little-endian.</summary> private Boolean littleEndian; /// <summary>The colour components. Though most stuff will just loop an int from 0 to 4, this shows the order.</summary> private enum ColorComponent { Red = 0, Green = 1, Blue = 2, Alpha = 3 } /// <summary> /// Creats a new PixelFormatter, with automatic calculation of colour multipliers using the CalculateMultiplier function. /// </summary> /// <param name="bytesPerPixel">Amount of bytes to read per pixel</param> /// <param name="redBits">Amount of bits to read for the red colour component</param> /// <param name="redShift">Amount of bits to shift the data to get to the red colour component</param> /// <param name="greenBits">Amount of bits to read for the green colour component</param> /// <param name="greenShift">Amount of bits to shift the data to get to the green colour component</param> /// <param name="blueBits">Amount of bits to read for the blue colour component</param> /// <param name="blueShift">Amount of bits to shift the data to get to the blue colour component</param> /// <param name="alphaBits">Amount of bits to read for the alpha colour component</param> /// <param name="alphaShift">Amount of bits to shift the data to get to the alpha colour component</param> /// <param name="littleEndian">True if the read bytes are interpreted as little-endian.</param> public PixelFormatter(Byte bytesPerPixel, Byte redBits, Byte redShift, Byte greenBits, Byte greenShift, Byte blueBits, Byte blueShift, Byte alphaBits, Byte alphaShift, Boolean littleEndian) : this(bytesPerPixel, redBits, redShift, CalculateMultiplier(redBits), greenBits, greenShift, CalculateMultiplier(greenBits), blueBits, blueShift, CalculateMultiplier(blueBits), alphaBits, alphaShift, CalculateMultiplier(alphaBits), littleEndian) { } /// <summary> /// Creates a new PixelFormatter. /// </summary> /// <param name="bytesPerPixel">Amount of bytes to read per pixel</param> /// <param name="redBits">Amount of bits to read for the red colour component</param> /// <param name="redShift">Amount of bits to shift the data to get to the red colour component</param> /// <param name="redMultiplier">Multiplier for the red component's value to adjust it to the normal 0-255 range.</param> /// <param name="greenBits">Amount of bits to read for the green colour component</param> /// <param name="greenShift">Amount of bits to shift the data to get to the green colour component</param> /// <param name="greenMultiplier">Multiplier for the green component's value to adjust it to the normal 0-255 range.</param> /// <param name="blueBits">Amount of bits to read for the blue colour component</param> /// <param name="blueShift">Amount of bits to shift the data to get to the blue colour component</param> /// <param name="blueMultiplier">Multiplier for the blue component's value to adjust it to the normal 0-255 range.</param> /// <param name="alphaBits">Amount of bits to read for the alpha colour component</param> /// <param name="alphaShift">Amount of bits to shift the data to get to the alpha colour component</param> /// <param name="alphaMultiplier">Multiplier for the alpha component's value to adjust it to the normal 0-255 range.</param> /// <param name="littleEndian">True if the read bytes are interpreted as little-endian.</param> public PixelFormatter(Byte bytesPerPixel, Byte redBits, Byte redShift, Double redMultiplier, Byte greenBits, Byte greenShift, Double greenMultiplier, Byte blueBits, Byte blueShift, Double blueMultiplier, Byte alphaBits, Byte alphaShift, Double alphaMultiplier, Boolean littleEndian) { this.bytesPerPixel = bytesPerPixel; this.littleEndian = littleEndian; this.bitsAmounts [(Int32)ColorComponent.Red] = redBits; this.shiftAmounts[(Int32)ColorComponent.Red] = redShift; this.multipliers [(Int32)ColorComponent.Red] = redMultiplier; this.limitMasks[(Int32)ColorComponent.Red] = GetLimitMask(redBits, redShift); this.bitsAmounts [(Int32)ColorComponent.Green] = greenBits; this.shiftAmounts[(Int32)ColorComponent.Green] = greenShift; this.multipliers [(Int32)ColorComponent.Green] = greenMultiplier; this.limitMasks[(Int32)ColorComponent.Green] = GetLimitMask(greenBits, greenShift); this.bitsAmounts [(Int32)ColorComponent.Blue] = blueBits; this.shiftAmounts[(Int32)ColorComponent.Blue] = blueShift; this.multipliers [(Int32)ColorComponent.Blue] = blueMultiplier; this.limitMasks[(Int32)ColorComponent.Blue] = GetLimitMask(blueBits, blueShift); this.bitsAmounts [(Int32)ColorComponent.Alpha] = alphaBits; this.shiftAmounts[(Int32)ColorComponent.Alpha] = alphaShift; this.multipliers [(Int32)ColorComponent.Alpha] = alphaMultiplier; this.limitMasks[(Int32)ColorComponent.Alpha] = GetLimitMask(alphaBits, alphaShift); } private static UInt32 GetLimitMask(Byte bpp, Byte shift) { return (UInt32)(((1 << bpp) - 1) << shift); } /// <summary> /// Using this multiplier instead of a basic int ensures a true uniform distribution of values of this bits length over the 0-255 range. /// </summary> /// <param name="colorComponentBitLength">Bits length of the color component</param> /// <returns>The most correct multiplier to convert colour components of the given bits length to a 0-255 range.</returns> public static Double CalculateMultiplier(Byte colorComponentBitLength) { return 255.0 / ((1 << colorComponentBitLength) - 1); } public Color GetColor(Byte[] data, Int32 offset) { UInt32 value = ArrayUtils.ReadIntFromByteArray(data, offset, this.bytesPerPixel, this.littleEndian); return GetColorFromValue(value); } public void WriteColor(Byte[] data, Int32 offset, Color color) { UInt32 value = GetValueFromColor(color); ArrayUtils.WriteIntToByteArray(data, offset, this.bytesPerPixel, this.littleEndian, value); } public Color GetColorFromValue(UInt32 readValue) { Byte[] components = new Byte[4]; for (Int32 i = 0; i < 4; i++) { if (bitsAmounts[i] == 0) components[i] = defaults[i]; else components[i] = GetChannelFromValue(readValue, (ColorComponent)i); } return Color.FromArgb(components[(Int32)ColorComponent.Alpha], components[(Int32)ColorComponent.Red], components[(Int32)ColorComponent.Green], components[(Int32)ColorComponent.Blue]); } private Byte GetChannelFromValue(UInt32 readValue, ColorComponent type) { UInt32 val = (UInt32)(readValue & limitMasks[(Int32)type]); val = (UInt32)(val >> this.shiftAmounts[(Int32)type]); Double valD = (Double)(val * multipliers[(Int32)type]); return (Byte)Math.Min(255, Math.Round(valD, MidpointRounding.AwayFromZero)); } public UInt32 GetValueFromColor(Color color) { Byte[] components = new Byte[] { color.R, color.G, color.B, color.A}; UInt32 val = 0; for (Int32 i = 0; i < 4; i++) { UInt32 mask = (UInt32)((1 << bitsAmounts[i]) - 1); Double tempValD = (Double)components[i] / this.multipliers[i]; UInt32 tempVal = (Byte)Math.Min(mask, Math.Round(tempValD, MidpointRounding.AwayFromZero)); tempVal = (UInt32)(tempVal << this.shiftAmounts[i]); val |= tempVal; } return val; } }
函数负责获取0-255范围内的均匀分布值。不过,如果您想使用简单的方法(可能会在转换过程中导致较小的舍入错误)可以使用更复杂的构造函数手动给出所有乘数 最后的字节然后被插入到新创建的32位映像中,用于访问和写入映像的底层字节 哦,下面是我的CalculateMultiplier
类中提到的ArrayUtils
和readintfromtearray
:WriteIntToByteArray
public static UInt32 ReadIntFromByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian) { Int32 lastByte = bytes - 1; if (data.Length < startIndex + bytes) throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset" + startIndex + "."); UInt32 value = 0; for (Int32 index = 0; index < bytes; index++) { Int32 offs = startIndex + (littleEndian ? index : lastByte - index); value += (UInt32)(data[offs] << (8 * index)); } return value; } public static void WriteIntToByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian, UInt32 value) { Int32 lastByte = bytes - 1; if (data.Length < startIndex + bytes) throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset" + startIndex + "."); for (Int32 index = 0; index < bytes; index++) { Int32 offs = startIndex + (littleEndian ? index : lastByte - index); data[offs] = (Byte)(value >> (8 * index) & 0xFF); } }
最有可能是这样。如果您的问题实际上是“如何?”,您可能希望解释“处理和提取”的含义,并展示您的尝试。是的,问题应该是“如何”我已经编辑过这篇文章,用于访问MrSID文件?public static UInt32 ReadIntFromByteArray(字节[]数据、Int32 startIndex、Int32字节、布尔littleEndian) { Int32 lastByte=字节-1; if(data.Length
(8*索引)和0xFF); } }