2016-02-19 18 views
5

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

+0

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

+0

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. –

Trả lời

2

JPEG màu mẫu phụ có thể được thực hiện một cách đơn giản, tuy nhiên chức năng mà không có nhiều mã. Ý tưởng cơ bản là đôi mắt của bạn ít nhạy cảm với những thay đổi về màu sắc so với những thay đổi về độ sáng, do đó, tệp JPEG có thể nhỏ hơn nhiều bằng cách vứt bỏ một số thông tin màu sắc. Có nhiều cách để lấy mẫu thông tin màu, nhưng hình ảnh JPEG có xu hướng sử dụng 4 biến thể: none, 1/2 horizontal, 1/2 vertical và 1/2 horizontal + vertical. Có các tùy chọn TIFF/EXIF ​​bổ sung chẳng hạn như "điểm trung tâm" của màu được lấy mẫu phụ, nhưng để đơn giản, chúng tôi sẽ sử dụng trung bình kỹ thuật tổng hợp.

Trong trường hợp đơn giản nhất (không có subsampling), mỗi MCU (đơn vị mã hóa tối thiểu) là một khối 8x8 pixel được tạo thành từ 3 thành phần - Y, Cb, Cr. Hình ảnh được xử lý trong các khối 8x8 pixel, trong đó 3 thành phần màu được tách ra, chuyển qua một biến đổi DCT và được ghi vào tệp theo thứ tự (Y, Cb, Cr). Trong tất cả các trường hợp lấy mẫu con, các khối DCT luôn bao gồm hệ số 8x8 hoặc 64 giá trị, nhưng ý nghĩa của các giá trị đó thay đổi do việc lấy mẫu màu.

Trường hợp đơn giản nhất tiếp theo được đặt trong một thứ nguyên (ngang hoặc dọc). Hãy sử dụng 1/2 subsampling ngang cho ví dụ này. MCU hiện có chiều rộng 16 pixel x 8 pixel. Đầu ra nén của mỗi MCU bây giờ sẽ là 4 khối 8x1 DCT (Y0, Y1, Cb, Cr). Y0 đại diện cho giá trị luma của khối 8x8 pixel bên trái và Y1 đại diện cho giá trị luma của khối 8x8 pixel phù hợp. Giá trị Cb và Cr là mỗi khối 8x8 dựa trên giá trị trung bình của các cặp pixel ngang. Tôi không thể tìm thấy bất kỳ hình ảnh tốt để chèn ở đây, nhưng một số mã giả có thể có ích.

(Cập nhật: hình ảnh mà có thể đại diện cho mẫu phụ :) enter image description here

Dưới đây là một vòng lặp đơn giản mà hiện các mẫu phụ màu của 1/2 trường hợp ngang của chúng tôi:

unsigned char ucCb[8][8], ucCr[8][8]; 
int x, y; 

for (y=0; y<8; y++) 
{ 
    for (x=0; x<8; x++) 
    { 
     ucCb[y][x] = (srcCb[y][x*2] + srcCb[y][(x*2)+1] + 1)/2; // average each horiz pair 
     ucCr[y][x] = (srcCr[y][x*2] + srcCr[y][(x*2)+1] + 1)/2; 
    } // for x 
} // for y 

Như bạn thấy, có không nhiều. Mỗi cặp điểm ảnh Cb và Cr từ hình ảnh nguồn được tính trung bình theo chiều ngang để tạo thành một điểm ảnh Cb/Cr mới. Đây là sau đó DCT chuyển đổi, zigzagged và mã hóa trong cùng một hình thức như mọi khi.

Cuối cùng đối với trường hợp mẫu phụ 2x2, MCU hiện là 16x16 pixel và khối DCT được viết sẽ là Y0, Y1, Y2, Y3, Cb, Cr. Trong đó Y0 đại diện cho các pixel x8 trái phía trên 8x8, Y1 phía trên bên phải, Y2 phía dưới bên trái và Y3 phía dưới bên phải. Giá trị Cb và Cr trong trường hợp này đại diện cho 4 pixel nguồn (2x2) đã được tính trung bình cùng nhau. Chỉ trong trường hợp bạn đang tự hỏi, các giá trị màu được tính trung bình với nhau trong không gian màu YCbCr. Nếu bạn trung bình các pixel với nhau trong không gian màu RGB, nó sẽ không hoạt động chính xác.

FYI - Adobe hỗ trợ hình ảnh JPEG trong không gian màu RGB (thay vì YCbCr). Những hình ảnh này không thể sử dụng màu subsampling vì R, G và B có tầm quan trọng như nhau và subsampling chúng trong không gian màu này sẽ dẫn đến hiện vật trực quan tồi tệ hơn nhiều.

+0

Ah ... Tôi nghĩ tôi có nó ngay bây giờ. Nó đã cho tôi một vài lần đọc để tìm ra rằng lấy mẫu ngang trong mã ví dụ của bạn dọc theo x là [0-1] [2-3] [4-5] [6-7] [8-9] [10-11] [12-13] [14-15] lặp lại. Tôi sẽ thực hiện nó. –

+0

@BitBank nếu hình ảnh của bạn có kích thước 8 pixel x 8pixels và MCU của bạn là 16x16 hoặc 16x8. Nó sẽ hoạt động như thế nào? Và nếu hình ảnh của bạn không phải là bội số của 8 thì sao? – juFo

+0

@juFo - trong trường hợp này, phần không sử dụng của MCU sẽ bị bộ mã hóa và bộ giải mã bỏ qua. MCU 16x16 mã hóa hình ảnh 8x8 hoàn toàn hợp lệ. Thật không may trong trường hợp này, bạn vẫn cần mã hóa một MCU hoàn chỉnh được tạo thành từ 6 khối 8x1 DCT (y0, y1, y2, y3, Cb, Cr). Nó có lẽ không có giá trị subsampling màu trong trường hợp này. – BitBank

Các vấn đề liên quan