MENU

【HEVC学習】5. HMのTVideoIOYuvクラス

H.265/HEVC(High Efficiency Video Coding)のリファレンスソフトウェア(HM)のTVideoIOYuvクラスについて学んでいきます。前提として入力画像はQCIF(176×144)、YCbCr 4:2:0、configファイルはcfg/misc以下のencoder_randomaccess_main_GOP8.cfgを用いるものとして話を進めます。

目次

TVideoIOYuv::open()

TVideoIOYuv::open()ではTVideoIOYuvクラスの内部変数m_fileBitdepth、m_MSBExtendedBitDepth、m_bitdepthShiftを初期化し、m_cHandleにOpenしたファイルを保持します。

m_fileBitDepth

入力映像のビット深度です。configファイル上ではInputBitDepthというパラメータで指定します。

m_MSBExtendedBitDepth

入力映像の各画素に対してm_MSBExtendedBitDepthに指定したビット深度となるよう値0でMSBが拡張されます。configファイル上ではMSBExtendedBitDepthというパラメータで指定し、MSBExtendedBitDepth >= InputBitDepthである必要があります。

m_bitdepthShift

TVideoIOYuv::scalePlane()内でInternalBitDepthで指定したビット深度となるよう入力映像の各画素をビットシフトする際のシフト量(InternalBitDepth - MSBExtendedBitDepth)です。

/**
 * Open file for reading/writing Y'CbCr frames.
 *
 * Frames read/written have bitdepth fileBitDepth, and are automatically
 * formatted as 8 or 16 bit word values (see TVideoIOYuv::write()).
 *
 * Image data read or written is converted to/from internalBitDepth
 * (See scalePlane(), TVideoIOYuv::read() and TVideoIOYuv::write() for
 * further details).
 *
 * \param pchFile          file name string
 * \param bWriteMode       file open mode: true=write, false=read
 * \param fileBitDepth     bit-depth array of input/output file data.
 * \param MSBExtendedBitDepth
 * \param internalBitDepth bit-depth array to scale image data to/from when reading/writing.
 */
Void TVideoIOYuv::open( const std::string &fileName, Bool bWriteMode, const Int fileBitDepth[MAX_NUM_CHANNEL_TYPE], const Int MSBExtendedBitDepth[MAX_NUM_CHANNEL_TYPE], const Int internalBitDepth[MAX_NUM_CHANNEL_TYPE] )
{
  //NOTE: files cannot have bit depth greater than 16
  for(UInt ch=0; ch<MAX_NUM_CHANNEL_TYPE; ch++)
  {
    m_fileBitdepth       [ch] = std::min<UInt>(fileBitDepth[ch], 16);
    m_MSBExtendedBitDepth[ch] = MSBExtendedBitDepth[ch];
    m_bitdepthShift      [ch] = internalBitDepth[ch] - m_MSBExtendedBitDepth[ch];

    if (m_fileBitdepth[ch] > 16)
    {
      if (bWriteMode)
      {
        std::cerr << "\nWARNING: Cannot write a yuv file of bit depth greater than 16 - output will be right-shifted down to 16-bit precision\n" << std::endl;
      }
      else
      {
        std::cerr << "\nERROR: Cannot read a yuv file of bit depth greater than 16\n" << std::endl;
        exit(0);
      }
    }
  }

  if ( bWriteMode )
  {
    m_cHandle.open( fileName.c_str(), ios::binary | ios::out );

    if( m_cHandle.fail() )
    {
      printf("\nfailed to write reconstructed YUV file\n");
      exit(0);
    }
  }
  else
  {
    m_cHandle.open( fileName.c_str(), ios::binary | ios::in );

    if( m_cHandle.fail() )
    {
      printf("\nfailed to open Input YUV file\n");
      exit(0);
    }
  }

  return;
}

TVideoIOYuv::read()

TVideoIOYuv::read()はファイルストリームから1フレーム分の画像を読み込み、InternalBitDepthで指定したビット深度となるよう画素値を調整し、色成分の並び替えを行います。色成分の並び替え後の画像は引数pPicYuvUserに、並び替え前の画像は引数pPicYuvTrueOrgに格納されます。

/**
 * Read one Y'CbCr frame, performing any required input scaling to change
 * from the bitdepth of the input file to the internal bit-depth.
 *
 * If a bit-depth reduction is required, and internalBitdepth >= 8, then
 * the input file is assumed to be ITU-R BT.601/709 compliant, and the
 * resulting data is clipped to the appropriate legal range, as if the
 * file had been provided at the lower-bitdepth compliant to Rec601/709.
 *
 * @param pPicYuvUser      input picture YUV buffer class pointer
 * @param pPicYuvTrueOrg
 * @param ipcsc
 * @param aiPad            source padding size, aiPad[0] = horizontal, aiPad[1] = vertical
 * @param format           chroma format
 * @return true for success, false in case of error
 */
Bool TVideoIOYuv::read ( TComPicYuv*  pPicYuvUser, TComPicYuv* pPicYuvTrueOrg, const InputColourSpaceConversion ipcsc, Int aiPad[2], ChromaFormat format, const Bool bClipToRec709 )
{
  // check end-of-file
  if ( isEof() )
  {
    return false;
  }
  TComPicYuv *pPicYuv=pPicYuvTrueOrg;
  if (format>=NUM_CHROMA_FORMAT)
  {
    format=pPicYuv->getChromaFormat();
  }

  Bool is16bit = false;

  for(UInt ch=0; ch<MAX_NUM_CHANNEL_TYPE; ch++)
  {
    if (m_fileBitdepth[ch] > 8)
    {
      is16bit=true;
    }
  }

  // バッファ領域の幅(336)
  const UInt stride444      = pPicYuv->getStride(COMPONENT_Y);

  // compute actual YUV width & height excluding padding size
  const UInt pad_h444       = aiPad[0];
  const UInt pad_v444       = aiPad[1];

  // ConformanceWindowModeが1以上の場合, 有効画像サイズが最小CUサイズの倍数でないとき等にパディングが追加される
  // パディング込みの有効画像領域の幅(176)
  const UInt width_full444  = pPicYuv->getWidth(COMPONENT_Y);
  // パディング込みの有効画像領域の高さ(144)
  const UInt height_full444 = pPicYuv->getHeight(COMPONENT_Y);

  // パディングなしの有効画像領域の幅(176)
  const UInt width444       = width_full444 - pad_h444;
  // パディングなしの有効画像領域の幅(144)
  const UInt height444      = height_full444 - pad_v444;

  for(UInt comp=0; comp<MAX_NUM_COMPONENT; comp++)
  {
    const ComponentID compID = ComponentID(comp);
    const ChannelType chType=toChannelType(compID);

    // desired_bitdepth=InternalBitDepth
    const Int desired_bitdepth = m_MSBExtendedBitDepth[chType] + m_bitdepthShift[chType];

    // ClipInputVideoToRec709Range=1かつInternalBitDepth < MSBExtendedBitDepthの場合(10bit画像を8bitで処理する場合等)
    // InternalBitDepth=8の場合, Rec.709では0と255は画素値として扱えないためminval=1, maxval=254となる
    // InternalBitDepth=10の場合, minval=4(=1<<2), maxval=1019(=255<<2-1)となる
    const Bool b709Compliance=(bClipToRec709) && (m_bitdepthShift[chType] < 0 && desired_bitdepth >= 8);     /* ITU-R BT.709 compliant clipping for converting say 10b to 8b */
    const Pel minval = b709Compliance? ((   1 << (desired_bitdepth - 8))   ) : 0;
    const Pel maxval = b709Compliance? ((0xff << (desired_bitdepth - 8)) -1) : (1 << desired_bitdepth) - 1;

    // m_cHandleで指定したファイルストリームから画像を読み込み, pPicYuv->getAddr()により得られるm_piPicOrg(有効画像領域先頭へのポインタ)へ格納する
    // パディングされた領域も画像端の画素値で埋められる
    if (! readPlane(pPicYuv->getAddr(compID), m_cHandle, is16bit, stride444, width444, height444, pad_h444, pad_v444, compID, pPicYuv->getChromaFormat(), format, m_fileBitdepth[chType]))
    {
      return false;
    }

    // ビット深度の変更を行う
    if (compID < pPicYuv->getNumberValidComponents() )
    {
      const UInt csx=getComponentScaleX(compID, pPicYuv->getChromaFormat());
      const UInt csy=getComponentScaleY(compID, pPicYuv->getChromaFormat());
      scalePlane(pPicYuv->getAddr(compID), stride444>>csx, width_full444>>csx, height_full444>>csy, m_bitdepthShift[chType], minval, maxval);
    }
  }

  // 色成分の並び替えを行う
  if(pPicYuvUser)
  {
    ColourSpaceConvert(*pPicYuvTrueOrg, *pPicYuvUser, ipcsc, true);
  }

  return true;
}

TVideoIOYuv::write()

TVideoIOYuv::write()はバッファ領域に格納されている画像に対して色成分の並び替えを行い、MSBExtendedBitDepthで指定したビット深度となるよう画素値を調整し、ファイルストリームへ書き出します。

/**
 * Write one Y'CbCr frame. No bit-depth conversion is performed, pcPicYuv is
 * assumed to be at TVideoIO::m_fileBitdepth depth.
 *
 * @param pPicYuvUser      input picture YUV buffer class pointer
 * @param ipCSC
 * @param confLeft         conformance window left border
 * @param confRight        conformance window right border
 * @param confTop          conformance window top border
 * @param confBottom       conformance window bottom border
 * @param format           chroma format
 * @return true for success, false in case of error
 */
Bool TVideoIOYuv::write( TComPicYuv* pPicYuvUser, const InputColourSpaceConversion ipCSC, Int confLeft, Int confRight, Int confTop, Int confBottom, ChromaFormat format, const Bool bClipToRec709 )
{
  TComPicYuv cPicYuvCSCd;
  // 色成分の並び替え
  if (ipCSC!=IPCOLOURSPACE_UNCHANGED)
  {
    cPicYuvCSCd.createWithoutCUInfo(pPicYuvUser->getWidth(COMPONENT_Y), pPicYuvUser->getHeight(COMPONENT_Y), pPicYuvUser->getChromaFormat() );
    ColourSpaceConvert(*pPicYuvUser, cPicYuvCSCd, ipCSC, false);
  }
  TComPicYuv *pPicYuv=(ipCSC==IPCOLOURSPACE_UNCHANGED) ? pPicYuvUser : &cPicYuvCSCd;

  // compute actual YUV frame size excluding padding size
  Bool is16bit = false;
  Bool nonZeroBitDepthShift=false;

  for(UInt ch=0; ch<MAX_NUM_CHANNEL_TYPE; ch++)
  {
    if (m_fileBitdepth[ch] > 8)
    {
      is16bit=true;
    }
    if (m_bitdepthShift[ch] != 0)
    {
      nonZeroBitDepthShift=true;
    }
  }

  TComPicYuv *dstPicYuv = NULL;
  Bool retval = true;
  if (format>=NUM_CHROMA_FORMAT)
  {
    format=pPicYuv->getChromaFormat();
  }

  // read()とは逆にInternalBitDepthからMSBExtendedBitDepthとなるようビット深度を変更する
  if (nonZeroBitDepthShift)
  {
    dstPicYuv = new TComPicYuv;
    dstPicYuv->createWithoutCUInfo( pPicYuv->getWidth(COMPONENT_Y), pPicYuv->getHeight(COMPONENT_Y), pPicYuv->getChromaFormat() );

    for(UInt comp=0; comp<dstPicYuv->getNumberValidComponents(); comp++)
    {
      const ComponentID compID=ComponentID(comp);
      const ChannelType ch=toChannelType(compID);
      const Bool b709Compliance = bClipToRec709 && (-m_bitdepthShift[ch] < 0 && m_MSBExtendedBitDepth[ch] >= 8);     /* ITU-R BT.709 compliant clipping for converting say 10b to 8b */
      const Pel minval = b709Compliance? ((   1 << (m_MSBExtendedBitDepth[ch] - 8))   ) : 0;
      const Pel maxval = b709Compliance? ((0xff << (m_MSBExtendedBitDepth[ch] - 8)) -1) : (1 << m_MSBExtendedBitDepth[ch]) - 1;

      copyPlane(*pPicYuv, compID, *dstPicYuv, compID);
      scalePlane(dstPicYuv->getAddr(compID), dstPicYuv->getStride(compID), dstPicYuv->getWidth(compID), dstPicYuv->getHeight(compID), -m_bitdepthShift[ch], minval, maxval);
    }
  }
  else
  {
    dstPicYuv = pPicYuv;
  }

  // バッファ領域の幅(336)
  const Int  stride444 = dstPicYuv->getStride(COMPONENT_Y);
  // パディングを除いた元々の画像サイズを算出
  const UInt width444  = dstPicYuv->getWidth(COMPONENT_Y) - confLeft - confRight;
  const UInt height444 = dstPicYuv->getHeight(COMPONENT_Y) -  confTop  - confBottom;

  if ((width444 == 0) || (height444 == 0))
  {
    printf ("\nWarning: writing %d x %d luma sample output picture!", width444, height444);
  }

  for(UInt comp=0; retval && comp<dstPicYuv->getNumberValidComponents(); comp++)
  {
    const ComponentID compID = ComponentID(comp);
    const ChannelType ch=toChannelType(compID);
    const UInt csx = dstPicYuv->getComponentScaleX(compID);
    const UInt csy = dstPicYuv->getComponentScaleY(compID);
    // 画像の上端, 左端に追加したパディング領域分のアドレスを飛ばすためのオフセットを算出
    const Int planeOffset =  (confLeft>>csx) + (confTop>>csy) * dstPicYuv->getStride(compID);
    // dstPicYuv->getAddr()により得られるm_piPicOrg(有効画像領域先頭へのポインタ)から画像を読み込み, m_cHandleで指定したファイルストリームへ書き出す
    if (! writePlane(m_cHandle, dstPicYuv->getAddr(compID) + planeOffset, is16bit, stride444, width444, height444, compID, dstPicYuv->getChromaFormat(), format, m_fileBitdepth[ch]))
    {
      retval=false;
    }
  }

  if (nonZeroBitDepthShift)
  {
    dstPicYuv->destroy();
    delete dstPicYuv;
  }

  cPicYuvCSCd.destroy();

  return retval;
}

TVideoIOYuv::ColourSpaceConvert()

TVideoIOYuv::ColourSpaceConvert()は入力画像に対して色成分の並び替えを行い、copyPlane()により結果を引数destにコピーします。source/Lib/TLibCommon/TypeDef.hのコメントを見ると、RGBtoGBR以外の並び替えは主にデバッグ用に用いられるようです。

Void TVideoIOYuv::ColourSpaceConvert(const TComPicYuv &src, TComPicYuv &dest, const InputColourSpaceConversion conversion, Bool bIsForwards)
{
  const ChromaFormat  format=src.getChromaFormat();
  const UInt          numValidComp=src.getNumberValidComponents();

  switch (conversion)
  {
    case IPCOLOURSPACE_YCbCrtoYYY:
      if (format!=CHROMA_444)
      {
        // only 444 is handled.
        assert(format==CHROMA_444);
        exit(1);
      }

      {
        for(UInt comp=0; comp<numValidComp; comp++)
        {
          copyPlane(src, ComponentID(bIsForwards?0:comp), dest, ComponentID(comp));
        }
      }
      break;
    case IPCOLOURSPACE_YCbCrtoYCrCb:
      {
        for(UInt comp=0; comp<numValidComp; comp++)
        {
          copyPlane(src, ComponentID(comp), dest, ComponentID((numValidComp-comp)%numValidComp));
        }
      }
      break;

    case IPCOLOURSPACE_RGBtoGBR:
      {
        if (format!=CHROMA_444)
        {
          // only 444 is handled.
          assert(format==CHROMA_444);
          exit(1);
        }

        // channel re-mapping
        for(UInt comp=0; comp<numValidComp; comp++)
        {
          const ComponentID compIDsrc=ComponentID((comp+1)%numValidComp);
          const ComponentID compIDdst=ComponentID(comp);
          copyPlane(src, bIsForwards?compIDsrc:compIDdst, dest, bIsForwards?compIDdst:compIDsrc);
        }
      }
      break;

    case IPCOLOURSPACE_UNCHANGED:
    default:
      {
        for(UInt comp=0; comp<numValidComp; comp++)
        {
          copyPlane(src, ComponentID(comp), dest, ComponentID(comp));
        }
      }
      break;
  }
}

readPlane()

readPlane()はファイルストリームから画素値を読み込み、dstで指定されたバッファ領域へと書き込みます。読み込むファイルや出力先バッファの色差フォーマットに応じて、画素を間引いたり、あるいは画素のコピーを行いながら処理を行います。右端と下端のパディング領域に対する処理も併せて行います。

/**
 * Read width*height pixels from fd into dst, optionally
 * padding the left and right edges by edge-extension.  Input may be
 * either 8bit or 16bit little-endian lsb-aligned words.
 *
 * @param dst          destination image plane
 * @param fd           input file stream
 * @param is16bit      true if input file carries > 8bit data, false otherwise.
 * @param stride444    distance between vertically adjacent pixels of dst.
 * @param width444     width of active area in dst.
 * @param height444    height of active area in dst.
 * @param pad_x444     length of horizontal padding.
 * @param pad_y444     length of vertical padding.
 * @param compID       chroma component
 * @param destFormat   chroma format of image
 * @param fileFormat   chroma format of file
 * @param fileBitDepth component bit depth in file
 * @return true for success, false in case of error
 */
static Bool readPlane(Pel* dst,
                      istream& fd,
                      Bool is16bit,
                      UInt stride444,
                      UInt width444,
                      UInt height444,
                      UInt pad_x444,
                      UInt pad_y444,
                      const ComponentID compID,
                      const ChromaFormat destFormat,
                      const ChromaFormat fileFormat,
                      const UInt fileBitDepth)
{
  // Yの場合0, CbCrの場合1となる
  const UInt csx_file =getComponentScaleX(compID, fileFormat);
  const UInt csy_file =getComponentScaleY(compID, fileFormat);
  const UInt csx_dest =getComponentScaleX(compID, destFormat);
  const UInt csy_dest =getComponentScaleY(compID, destFormat);

  // 有効画像領域サイズのスケーリング
  const UInt width_dest       = width444 >>csx_dest;
  const UInt height_dest      = height444>>csy_dest;
  // パディングサイズのスケーリング
  const UInt pad_x_dest       = pad_x444>>csx_dest;
  const UInt pad_y_dest       = pad_y444>>csy_dest;
  // バッファ領域幅のスケーリング
  const UInt stride_dest      = stride444>>csx_dest;

  // パディング込みの有効画像領域のサイズを算出
  const UInt full_width_dest  = width_dest+pad_x_dest;
  const UInt full_height_dest = height_dest+pad_y_dest;

  // ファイルストリーム中の有効画像領域の幅に相当するバイト数を算出
  const UInt stride_file      = (width444 * (is16bit ? 2 : 1)) >> csx_file;
  // stride_file分のバッファ領域を確保
  std::vector<UChar> bufVec(stride_file);
  UChar *buf=&(bufVec[0]);

  if (compID!=COMPONENT_Y && (fileFormat==CHROMA_400 || destFormat==CHROMA_400))
  {
    // 読み込むファイルがCHROMA_400でかつ, 出力先のフォーマットがCHROMA_400でない場合
    // 当該色差プレーンの全画素値を中間値とする
    if (destFormat!=CHROMA_400)
    {
      // set chrominance data to mid-range: (1<<(fileBitDepth-1))
      const Pel value=Pel(1<<(fileBitDepth-1));
      for (UInt y = 0; y < full_height_dest; y++, dst+=stride_dest)
      {
        for (UInt x = 0; x < full_width_dest; x++)
        {
          dst[x] = value;
        }
      }
    }

    // 読み込むファイルのフォーマットがCHROMA_400ではなく, かつ出力先のフォーマットがCHROMA_400である場合
    // ファイルポインタを当該色差プレーンの最後まで移動させて処理を強制終了する
    if (fileFormat!=CHROMA_400)
    {
      const UInt height_file      = height444>>csy_file;
      fd.seekg(height_file*stride_file, ios::cur);
      if (fd.eof() || fd.fail() )
      {
        return false;
      }
    }
  }
  else
  {
    // Yの場合0, CbCrの場合1となる
    const UInt mask_y_file=(1<<csy_file)-1;
    const UInt mask_y_dest=(1<<csy_dest)-1;
    for(UInt y444=0; y444<height444; y444++)
    {
      // Yの場合は常にif文中を実行, CbCrの場合はy444が奇数のときのみ実行され垂直方向の処理が間引かれる
      if ((y444&mask_y_file)==0)
      {
        // read a new line
        fd.read(reinterpret_cast<TChar*>(buf), stride_file);
        if (fd.eof() || fd.fail() )
        {
          return false;
        }
      } // if ((y444&mask_y_file)==0)

      // Yの場合は常にif文中を実行, CbCrの場合はy444が奇数のときのみ実行され垂直方向の処理が間引かれる
      if ((y444&mask_y_dest)==0)
      {
        // process current destination line
        if (csx_file < csx_dest)
        {
          // 読み込むファイルの色差の水平解像度 > 出力先の色差の水平解像度の場合
          // ex. 4:4:4を読み込んで4:2:2や4:2:0に出力

          // eg file is 444, dest is 422.
          const UInt sx=csx_dest-csx_file;
          if (!is16bit)
          {
            // 1画素ずつ間引かれてdstに格納される
            for (UInt x = 0; x < width_dest; x++)
            {
              dst[x] = buf[x<<sx];
            }
          }
          else
          {
            // 1画素ずつ間引かれてdstに格納される
            for (UInt x = 0; x < width_dest; x++)
            {
              dst[x] = Pel(buf[(x<<sx)*2+0]) | (Pel(buf[(x<<sx)*2+1])<<8);
            }
          }
        }
        else
        {
          // 読み込むファイルの色差の水平解像度 >= 出力先の色差の水平解像度の場合
          // eg file is 422, dest is 444.
          const UInt sx=csx_file-csx_dest;
          if (!is16bit)
          {
            // sx=1の場合は2画素ずつ同じ値がdstに格納される
            for (UInt x = 0; x < width_dest; x++)
            {
              dst[x] = buf[x>>sx];
            }
          }
          else
          {
            // sx=1の場合は2画素ずつ同じ値がdstに格納される
            for (UInt x = 0; x < width_dest; x++)
            {
              dst[x] = Pel(buf[(x>>sx)*2+0]) | (Pel(buf[(x>>sx)*2+1])<<8);
            }
          }
        }

        // process right hand side padding
        // 水平方向のパディング領域が右端画素値で埋められる
        const Pel val=dst[width_dest-1];
        for (UInt x = width_dest; x < full_width_dest; x++)
        {
          dst[x] = val;
        }

        // 次のラインにポインタを移す
        dst += stride_dest;
      } // if ((y444&mask_y_dest)==0)
    } // for(UInt y444=0; y444<height444; y444++)

    // process lower padding
    // 垂直方向のパディング領域が下端ラインの画素値で埋められる
    for (UInt y = height_dest; y < full_height_dest; y++, dst+=stride_dest)
    {
      for (UInt x = 0; x < full_width_dest; x++)
      {
        // (dst - stride_dest)は一つ前のラインへのポインタ
        dst[x] = (dst - stride_dest)[x];
      }
    }
  }
  return true;
}

writePlane()

writePlane()はバッファ領域から画素値を読み込み、fdで指定されたファイルストリームへと書き込みます。バッファ領域や出力ファイルの色差フォーマットに応じて、画素を間引いたり、あるいは画素のコピーを行いながら処理を行います。writePlane()の引数にはパディングを除いた画像サイズを指定する必要があります。

/**
 * Write an image plane (width444*height444 pixels) from src into output stream fd.
 *
 * @param fd         output file stream
 * @param src        source image
 * @param is16bit    true if input file carries > 8bit data, false otherwise.
 * @param stride444  distance between vertically adjacent pixels of src.
 * @param width444   width of active area in src.
 * @param height444  height of active area in src.
 * @param compID       chroma component
 * @param srcFormat    chroma format of image
 * @param fileFormat   chroma format of file
 * @param fileBitDepth component bit depth in file
 * @return true for success, false in case of error
 */
static Bool writePlane(ostream& fd, Pel* src, Bool is16bit,
                       UInt stride444,
                       UInt width444, UInt height444,
                       const ComponentID compID,
                       const ChromaFormat srcFormat,
                       const ChromaFormat fileFormat,
                       const UInt fileBitDepth)
{
  // Yの場合0, CbCrの場合1となる
  const UInt csx_file =getComponentScaleX(compID, fileFormat);
  const UInt csy_file =getComponentScaleY(compID, fileFormat);
  const UInt csx_src  =getComponentScaleX(compID, srcFormat);
  const UInt csy_src  =getComponentScaleY(compID, srcFormat);

  // バッファ領域幅のスケーリング
  const UInt stride_src      = stride444>>csx_src;

  // ファイル出力画像の幅に相当するバイト数を算出
  const UInt stride_file      = (width444 * (is16bit ? 2 : 1)) >> csx_file;
  // ファイル出力画像サイズのスケーリング
  const UInt width_file       = width444 >>csx_file;
  const UInt height_file      = height444>>csy_file;

  // stride_file分のバッファ領域を確保
  std::vector<UChar> bufVec(stride_file);
  UChar *buf=&(bufVec[0]);

  if (compID!=COMPONENT_Y && (fileFormat==CHROMA_400 || srcFormat==CHROMA_400))
  {
    // 書き出すファイルがCHROMA_400ではなく, かつ入力のフォーマットがCHROMA_400である場合
    // 当該色差プレーンの全画素値を中間値とする
    if (fileFormat!=CHROMA_400)
    {
      const UInt value=1<<(fileBitDepth-1);

      for(UInt y=0; y< height_file; y++)
      {
        if (!is16bit)
        {
          UChar val(value);
          for (UInt x = 0; x < width_file; x++)
          {
            buf[x]=val;
          }
        }
        else
        {
          UShort val(value);
          for (UInt x = 0; x < width_file; x++)
          {
            buf[2*x+0]= (val>>0) & 0xff;
            buf[2*x+1]= (val>>8) & 0xff;
          }
        }

        fd.write(reinterpret_cast<const TChar*>(buf), stride_file);
        if (fd.eof() || fd.fail() )
        {
          return false;
        }
      } // for(UInt y=0; y< height_file; y++)
    } // if (fileFormat!=CHROMA_400)
  }
  else
  {
    // Yの場合0, CbCrの場合1となる
    const UInt mask_y_file=(1<<csy_file)-1;
    const UInt mask_y_src =(1<<csy_src )-1;
    for(UInt y444=0; y444<height444; y444++)
    {
      // Yの場合は常にif文中を実行, CbCrの場合はy444が奇数のときのみ実行され垂直方向の処理が間引かれる
      if ((y444&mask_y_file)==0)
      {
        // write a new line
        if (csx_file < csx_src)
        {
          // 入力バッファの色差の水平解像度 < 出力先ファイルの色差の水平解像度の場合
          // eg file is 444, source is 422.
          const UInt sx=csx_src-csx_file;
          if (!is16bit)
          {
            // 2画素ずつ同じ値がbufに格納される
            for (UInt x = 0; x < width_file; x++)
            {
              buf[x] = (UChar)(src[x>>sx]);
            }
          }
          else
          {
            // 2画素ずつ同じ値がbufに格納される
            for (UInt x = 0; x < width_file; x++)
            {
              buf[2*x  ] = (src[x>>sx]>>0) & 0xff;
              buf[2*x+1] = (src[x>>sx]>>8) & 0xff;
            }
          }
        }
        else
        {
          // 入力バッファの色差の水平解像度 >= 出力先ファイルの色差の水平解像度の場合
          // eg file is 422, src is 444.
          const UInt sx=csx_file-csx_src;
          if (!is16bit)
          {
            // 1画素ずつ間引かれbufに格納される
            for (UInt x = 0; x < width_file; x++)
            {
              buf[x] = (UChar)(src[x<<sx]);
            }
          }
          else
          {
            // 1画素ずつ間引かれbufに格納される
            for (UInt x = 0; x < width_file; x++)
            {
              buf[2*x  ] = (src[x<<sx]>>0) & 0xff;
              buf[2*x+1] = (src[x<<sx]>>8) & 0xff;
            }
          }
        }

        fd.write(reinterpret_cast<const TChar*>(buf), stride_file);
        if (fd.eof() || fd.fail() )
        {
          return false;
        }
      } // if ((y444&mask_y_file)==0)

      // Yの場合は常にif文中を実行, CbCrの場合はy444が奇数のときのみ実行される
      // バッファの次ラインにポインタを移す
      if ((y444&mask_y_src)==0)
      {
        src += stride_src;
      }

    }
  }
  return true;
}

scalePlane()

scalePlane()は引数shiftbitsの値に応じて画像のビット深度の変更を行います。

/**
 * Scale all pixels in img depending upon sign of shiftbits by a factor of
 * 2<sup>shiftbits</sup>.
 *
 * @param img        pointer to image to be transformed
 * @param stride  distance between vertically adjacent pixels of img.
 * @param width   width of active area in img.
 * @param height  height of active area in img.
 * @param shiftbits if zero, no operation performed
 *                  if > 0, multiply by 2<sup>shiftbits</sup>, see scalePlane()
 *                  if < 0, divide and round by 2<sup>shiftbits</sup> and clip,
 *                          see invScalePlane().
 * @param minval  minimum clipping value when dividing.
 * @param maxval  maximum clipping value when dividing.
 */
static Void scalePlane(Pel* img, const UInt stride, const UInt width, const UInt height, Int shiftbits, Pel minval, Pel maxval)
{
  if (shiftbits > 0)
  {
    // shiftbits > 0(InternalBitDepth > MSBExtendedBitDepth)の場合, 画素値をMSB側にシフト
    for (UInt y = 0; y < height; y++, img+=stride)
    {
      for (UInt x = 0; x < width; x++)
      {
        img[x] <<= shiftbits;
      }
    }
  }
  else if (shiftbits < 0)
  {
    // shiftbits < 0(InternalBitDepth < MSBExtendedBitDepth)の場合, 画素値をLSB側にシフト
    shiftbits=-shiftbits;

    Pel rounding = 1 << (shiftbits-1);
    for (UInt y = 0; y < height; y++, img+=stride)
    {
      for (UInt x = 0; x < width; x++)
      {
        // 画素値img[x]を指定されたビット数となるようshiftbitsで割る(丸め方は四捨五入)
        // 四捨五入後の値がminval, maxvalの範囲に収まるよう値をクリップする
        // Clip3()はsource/Lib/TLibCommon/CommonDef.hに定義されている
        img[x] = Clip3(minval, maxval, Pel((img[x] + rounding) >> shiftbits));
      }
    }
  }
}

copyPlane()

copyPlane()は引数srcに指定したバッファの有効画像領域を、引数destの有効画像領域にコピーします。

static Void
copyPlane(const TComPicYuv &src, const ComponentID srcPlane, TComPicYuv &dest, const ComponentID destPlane)
{
  const UInt width=src.getWidth(srcPlane);
  const UInt height=src.getHeight(srcPlane);
  assert(dest.getWidth(destPlane) == width);
  assert(dest.getHeight(destPlane) == height);
  const Pel *pSrc=src.getAddr(srcPlane);
  Pel *pDest=dest.getAddr(destPlane);
  const UInt strideSrc=src.getStride(srcPlane);
  const UInt strideDest=dest.getStride(destPlane);
  for(UInt y=0; y<height; y++, pSrc+=strideSrc, pDest+=strideDest)
  {
    memcpy(pDest, pSrc, width*sizeof(Pel));
  }
}
  • URLをコピーしました!

この記事を書いた人

映像処理ハードウェアの研究開発をしています。ASIC, FPGA, 機械学習などの話題に興味があります。このブログでは、自分が最近勉強したことなどを中心にマイペースに発信していきます。

目次