title: Chroma subsampling algorithm for jpeg
body: <p>I'm attempting to write a jpeg encoder and am stumbling at creating the algorithms that gather the appropriate Y, Cb, and Cr color components in order to pass to the method performing the transform.</p>
  
  <p>As I understand it for the four most common subsampling variants are setup as follows (I could be way off here):</p>
  
  <ul>
  <li>4:4:4 - An MCU block of 8x8 pixels with Y, Cb, and Cr represented in each pixel.</li>
  <li>4:2:2 - An MCU block of 16x8 pixels with Y in each pixel and Cb, Cr every two pixels</li>
  <li>4:2:0 - An MCU block of 16x16 pixels with Y every two pixels and Cb, Cr every four</li>
  </ul>
  
  <p>There most explicit description of the laout I have found so far is described <a href="http://www.impulseadventure.com/photo/jpeg-decoder.html" rel="noreferrer">here</a></p>
  
  <p>What I don't understand is how to gather those components in the correct order to pass as an 8x8 block for transforming and quantizing.</p>
  
  <p>Would someone be able to write an example, (pseudocode would be fine I'm sure, C# even better), of how to group the bytes for transform?</p>
  
  <p>I'll include the current, incorrect, code I am running.</p>
  
  <pre><code>/// &lt;summary&gt;
  /// Writes the Scan header structure
  /// &lt;/summary&gt;
  /// &lt;param name="image"&gt;The image to encode from.&lt;/param&gt;
  /// &lt;param name="writer"&gt;The writer to write to the stream.&lt;/param&gt;
  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 &lt; image.Height; y += 8)
      {
          // For each column
          for (int x = 0; x &lt; 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);
  }
  
  /// &lt;summary&gt;
  /// Converts the pixel block from the RGBA colorspace to YCbCr.
  /// &lt;/summary&gt;
  /// &lt;param name="image"&gt;&lt;/param&gt;
  /// &lt;param name="yComponant"&gt;The container to house the Y' luma componant within the block.&lt;/param&gt;
  /// &lt;param name="cbComponant"&gt;The container to house the Cb chroma componant within the block.&lt;/param&gt;
  /// &lt;param name="crComponant"&gt;The container to house the Cr chroma componant within the block.&lt;/param&gt;
  /// &lt;param name="x"&gt;The x-position within the image.&lt;/param&gt;
  /// &lt;param name="y"&gt;The y-position within the image.&lt;/param&gt;
  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 &lt; 8; a++)
      {
          // Complete with the remaining right and bottom edge pixels.
          int py = y + a;
          if (py &gt;= height)
          {
              py = height - 1;
          }
  
          for (int b = 0; b &lt; 8; b++)
          {
              int px = x + b;
              if (px &gt;= 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;
          }
      }
  }
  
  /// &lt;summary&gt;
  /// Compress and encodes the pixels. 
  /// &lt;/summary&gt;
  /// &lt;param name="componantValues"&gt;The current color component values within the image block.&lt;/param&gt;
  /// &lt;param name="componantIndex"&gt;The componant index.&lt;/param&gt;
  /// &lt;param name="writer"&gt;The writer.&lt;/param&gt;
  /// &lt;param name="dcValues"&gt;The descrete cosine values for each componant&lt;/param&gt;
  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 &lt; verticalFactors[componantIndex]; y++)
      {
          for (int x = 0; x &lt; 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];
          }
      }
  }
  </code></pre>
  
  <p>This code is part of an open source library I am writing on <a href="https://github.com/JimBobSquarePants/ImageProcessor" rel="noreferrer">Github</a></p>