Tôi đang cố gắng viết bộ mã hóa jpeg và đang dò tìm các thuật toán thu thập các thành phần màu Y, Cb và Cr thích hợp để chuyển sang phương thức thực hiện phép biến đổi.Thuật toán lấy mẫu Chroma cho jpeg
Theo tôi được biết trong bốn biến thể mẫu phụ phổ biến nhất được thiết lập như sau (tôi có thể là con đường tắt ở đây):
- 4: 4: 4 - Một khối MCU của pixel 8x8 với Y, Cb và Cr được biểu thị bằng từng pixel.
- 4: 2: 2 - Khối MCU có kích thước 16x8 pixel với Y trong mỗi pixel và Cb, Cr mỗi hai pixel
- 4: 2: 0 - Khối MCU có kích thước 16x16 pixel với Y mỗi hai pixel và Cb, Cr mỗi bốn
có nhất mô tả rõ ràng của laout tôi đã tìm thấy cho đến nay được mô tả here
những gì tôi không hiểu là làm thế nào để thu thập những thành phần theo thứ tự đúng để vượt qua như một khối 8x8 để chuyển đổi và định lượng.
Có ai đó có thể viết một ví dụ hay không, (giả mã có được không, tôi chắc chắn, C# tốt hơn), về cách nhóm các byte để chuyển đổi?
Tôi sẽ bao gồm mã hiện tại, không đúng, tôi đang chạy.
/// <summary>
/// Writes the Scan header structure
/// </summary>
/// <param name="image">The image to encode from.</param>
/// <param name="writer">The writer to write to the stream.</param>
private void WriteStartOfScan(ImageBase image, EndianBinaryWriter writer)
{
// Marker
writer.Write(new[] { JpegConstants.Markers.XFF, JpegConstants.Markers.SOS });
// Length (high byte, low byte), must be 6 + 2 * (number of components in scan)
writer.Write((short)0xc); // 12
byte[] sos = {
3, // Number of components in a scan, usually 1 or 3
1, // Component Id Y
0, // DC/AC Huffman table
2, // Component Id Cb
0x11, // DC/AC Huffman table
3, // Component Id Cr
0x11, // DC/AC Huffman table
0, // Ss - Start of spectral selection.
0x3f, // Se - End of spectral selection.
0 // Ah + Ah (Successive approximation bit position high + low)
};
writer.Write(sos);
// Compress and write the pixels
// Buffers for each Y'Cb Cr component
float[] yU = new float[64];
float[] cbU = new float[64];
float[] crU = new float[64];
// The descrete cosine values for each componant.
int[] dcValues = new int[3];
// TODO: Why null?
this.huffmanTable = new HuffmanTable(null);
// TODO: Color output is incorrect after this point.
// I think I've got my looping all wrong.
// For each row
for (int y = 0; y < image.Height; y += 8)
{
// For each column
for (int x = 0; x < image.Width; x += 8)
{
// Convert the 8x8 array to YCbCr
this.RgbToYcbCr(image, yU, cbU, crU, x, y);
// For each component
this.CompressPixels(yU, 0, writer, dcValues);
this.CompressPixels(cbU, 1, writer, dcValues);
this.CompressPixels(crU, 2, writer, dcValues);
}
}
this.huffmanTable.FlushBuffer(writer);
}
/// <summary>
/// Converts the pixel block from the RGBA colorspace to YCbCr.
/// </summary>
/// <param name="image"></param>
/// <param name="yComponant">The container to house the Y' luma componant within the block.</param>
/// <param name="cbComponant">The container to house the Cb chroma componant within the block.</param>
/// <param name="crComponant">The container to house the Cr chroma componant within the block.</param>
/// <param name="x">The x-position within the image.</param>
/// <param name="y">The y-position within the image.</param>
private void RgbToYcbCr(ImageBase image, float[] yComponant, float[] cbComponant, float[] crComponant, int x, int y)
{
int height = image.Height;
int width = image.Width;
for (int a = 0; a < 8; a++)
{
// Complete with the remaining right and bottom edge pixels.
int py = y + a;
if (py >= height)
{
py = height - 1;
}
for (int b = 0; b < 8; b++)
{
int px = x + b;
if (px >= width)
{
px = width - 1;
}
YCbCr color = image[px, py];
int index = a * 8 + b;
yComponant[index] = color.Y;
cbComponant[index] = color.Cb;
crComponant[index] = color.Cr;
}
}
}
/// <summary>
/// Compress and encodes the pixels.
/// </summary>
/// <param name="componantValues">The current color component values within the image block.</param>
/// <param name="componantIndex">The componant index.</param>
/// <param name="writer">The writer.</param>
/// <param name="dcValues">The descrete cosine values for each componant</param>
private void CompressPixels(float[] componantValues, int componantIndex, EndianBinaryWriter writer, int[] dcValues)
{
// TODO: This should be an option.
byte[] horizontalFactors = JpegConstants.ChromaFourTwoZeroHorizontal;
byte[] verticalFactors = JpegConstants.ChromaFourTwoZeroVertical;
byte[] quantizationTableNumber = { 0, 1, 1 };
int[] dcTableNumber = { 0, 1, 1 };
int[] acTableNumber = { 0, 1, 1 };
for (int y = 0; y < verticalFactors[componantIndex]; y++)
{
for (int x = 0; x < horizontalFactors[componantIndex]; x++)
{
// TODO: This can probably be combined reducing the array allocation.
float[] dct = this.fdct.FastFDCT(componantValues);
int[] quantizedDct = this.fdct.QuantizeBlock(dct, quantizationTableNumber[componantIndex]);
this.huffmanTable.HuffmanBlockEncoder(writer, quantizedDct, dcValues[componantIndex], dcTableNumber[componantIndex], acTableNumber[componantIndex]);
dcValues[componantIndex] = quantizedDct[0];
}
}
}
Mã này là một phần của một thư viện mã nguồn mở Tôi viết thư này Github
Liên kết mà bạn tham chiếu có thông tin tốt, nhưng bạn đã giải thích sai nó. Khi có màu phụ, kích thước pixel MCU thay đổi (ví dụ: 8x8, 16x8, 8x16, 16x16). Trong MCU đó, bạn cần lấy mẫu dữ liệu màu một cách thích hợp, sau đó sắp xếp nó thành các khối DCT 8x8 theo thứ tự được hiển thị trong bài viết (ví dụ: Y0, Y1, Y2, Y3, Cb, Cr) – BitBank
Cảm ơn vâng, tôi đoán rất nhiều. Tôi chỉ có thể không có được đầu của tôi xung quanh làm thế nào để thực hiện subsampling đó. I E. pixel nào lấy từ toàn bộ mảng pixel và thứ tự sắp xếp chúng trong mảng thành phần riêng lẻ của tôi. –