2021年6月15日火曜日

OpenCV imagecodes が color palette をモノクロ扱いしてしまう備忘録

前にも、pullreq を出そうとしたのだが、テストケースも含めて提案しないと取り込んでもらえなさそうだったので、ここに記す。

バージョンは opencv 4.5.2

imagecodes の grfmt_tiff.cpp
    if (tif)
    {
        uint32 wdth = 0, hght = 0;
        uint16 photometric = 0;

        CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &wdth));
        CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &hght));
        CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric));

        {
            bool isGrayScale = photometric == PHOTOMETRIC_MINISWHITE || photometric == PHOTOMETRIC_MINISBLACK;
            uint16 bpp = 8, ncn = isGrayScale ? 1 : 3;
            if (0 == TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bpp))
            {
                // TIFF bi-level images don't require TIFFTAG_BITSPERSAMPLE tag
                bpp = 1;
            }
//char buf[256];
//sprintf(buf, "%d, %d, %d\n", ncn, isGrayScale, bpp);
//OutputDebugStringA(buf);
// カラーパレットの場合 ncn = 3, isGrayScale = false, bpp = 8
            CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ncn));

//sprintf(buf, "%d, %d\n", ncn, photometric);
//OutputDebugStringA(buf);
// カラーパレットの場合 ncn = 1, photometric = PHOTOMETRIC_PALETTE
            m_width = wdth;
            m_height = hght;
            if (ncn == 3 && photometric == PHOTOMETRIC_LOGLUV)
            {
                m_type = CV_32FC3;
                m_hdr = true;
                return true;
            }
            m_hdr = false;

            if( bpp > 8 &&
               ((photometric > 2) ||
                (ncn != 1 && ncn != 3 && ncn != 4)))
                bpp = 8;

            int wanted_channels = normalizeChannelsNumber(ncn);

            // カラーパレットの画像がモノクロ扱いされないよう修正
            // ここから --->
            if( !isGrayScale && ncn == 1 && bpp == 8) {
              wanted_channels = 3;
            }
            // <--- ここまで
            switch(bpp)
            {
                case 1:
                    m_type = CV_MAKETYPE(CV_8U, !isGrayScale ? wanted_channels : 1);
                    result = true;
                    break;
                case 8:
                    m_type = CV_MAKETYPE(CV_8U, !isGrayScale ? wanted_channels : 1);
                    result = true;
                    break;
                case 16:
                    m_type = CV_MAKETYPE(CV_16U, !isGrayScale ? wanted_channels : 1);
                    result = true;
                    break;
                case 32:
                    m_type = CV_MAKETYPE(CV_32F, wanted_channels);
                    result = true;
                    break;
                case 64:
                    m_type = CV_MAKETYPE(CV_64F, wanted_channels);
                    result = true;
                    break;
            default:
                CV_Error(cv::Error::StsError, "Invalid bitsperpixel value read from TIFF header! Must be 1, 8, 16, 32 or 64.");
            }
        }
    }
とすることで、ちゃんとカラーで読み込まれる。

2021年6月3日木曜日

OpenCV アフィン変換の合成

OpenCV を使って、アフィン変換の合成をしようとしたら、うまく動作しなくてハマったので忘備録。

OpenCV で画像の回転をかけるアフィン変換を求める関数として、

  cv::getRotationMatrix2D という関数があります。

これが曲者、返されるのは 3 x 2 の行列で、アフィン変換の合成に使えない。
また、cv::warpAffine というのもあって、こいつも 3 x 2 の行列を受け取るアフィン変換の簡易バージョンで使えない

w1 x h1 の中心に alpha 回転し、横方向だけ n倍し、w2 x h2 のサイズに中心を移動したかったが、四苦八苦しました。

  cv::Mat ms = (cv::Mat_<double>(3,3) 
      << 1.0, 0.0, - w1 / 2.0,
         0.0, 1.0, - h1 / 2.0,
         0.0, 0.0, 1.0);
  cv::Mat rot = (cv::Mat_<double>(3,3)
      <<   cos(alpha), sin(alpha), 0.0,
         -sin(alpha), cos(alpha), 0.0,
         0.0,          0.0,         1.0);
  cv::Mat rat = (cv::Mat_<double>(3, 3)
      <<  n,  0.0, 0.0,
         0.0, 1.0, 0.0,
         0.0, 0.0, 1.0);
  cv::Mat mms = (cv::Mat_<double>(3,3)
      << 1.0, 0.0, w2 / 2.0,
      0.0, 1.0, h2 / 2.0,
      0.0, 0.0, 1.0);
  cv::Mat par = mms * rat * rot * ms;
  // src は (w1,h1)の入力画像
  // dst は (w2,h2)の出力画像
  cv::warpPerspective(src, dst, par, cv::Size(w2,h2), cv::INTER_LINEAR, cv::BORDER_TRANSPARENT);
とする事で、うまく合成変換できました。 #combine affine transform opencv