5

計画

私のプロジェクトは、ターゲット ウィンドウのビットマップをキャプチャして に変換し、IplImageそのイメージを に表示してcvNamedWindow、さらに処理を行うことができます。
テストのために、次のようにイメージを MSPaint にロードしました。

MSPaint テスト ウィンドウ

ユーザーは、イメージ内の任意の数のピクセル上でマウスをクリック アンド ドラッグして、vector<cv::Scalar_<BYTE>>これらの RGB カラー値を含む を作成できます。
次に、 の助けを借りてColorRGBToHLS()、この配列は次のように色相によって左から右にソートされます。

// PixelColor is just a cv::Scalar_<BYTE>
bool comparePixelColors( PixelColor& pc1, PixelColor& pc2 ) {
    WORD h1 = 0, h2 = 0;
    WORD s1 = 0, s2 = 0;
    WORD l1 = 0, l2 = 0;
    ColorRGBToHLS(RGB(pc1.val[2], pc1.val[1], pc1.val[0]), &h1, &l1, &s1);
    ColorRGBToHLS(RGB(pc2.val[2], pc2.val[1], pc2.val[0]), &h2, &l2, &s2);
    return ( h1 < h2 );
}

//..(elsewhere in code)
std::sort(m_colorRange.begin(), m_colorRange.end(), comparePixelColors);


...そして、次のような新しいcvNamedWindowに表示されます。

カラーレンジウィンドウ

問題

ここでのアイデアは、この選択された色の範囲が白になり、ソース画像の残りが黒になるバイナリしきい値画像(または「マスク」)を作成することです... 「色で選択」ツールの方法と同様GIMP で動作するか、「魔法の杖」ツールが Photoshop で動作します... ただし、特定の輪郭のある選択に限定するのではなく、文字通り画像全体を操作しています。

を読みましたがcvInRangeS、まさに私が必要としているもののようです。
ただし、何らかの理由で、しきい値処理された画像は常に完全に黒くなります...

VOID ShowThreshedImage(const IplImage* src, const PixelColor& min, const PixelColor& max)
{
    IplImage* imgHSV = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
    cvCvtColor(src, imgHSV, CV_RGB2HLS);
    cvNamedWindow("T1");
    cvShowImage("T1", imgHSV); // <-- Shows up like the image below

    IplImage* imgThreshed = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
    cvInRangeS(imgHSV, min, max, imgThreshed);
    cvNamedWindow("T2");
    cvShowImage("T2", imgThreshed); // <-- SHOWS UP PITCH BLACK!
}


これは、"T1"ウィンドウが最終的にどのように見えるかです (これは正しいと思いますか?) :

HSV結果ウィンドウ

色範囲ベクトルは RGB として格納されるため(そして OpenCV は内部的にこの順序を BGR に逆にするため)、最小値/最大値を HLS に変換してから次のように渡しますShowThreshedImage()

CvScalar rgbPixelToHSV(const PixelColor& pixelColor)
{
    WORD h = 0, s = 0, l = 0;
    ColorRGBToHLS(RGB(pixelColor.val[2], pixelColor.val[1], pixelColor.val[0]), &h, &l, &s);
    return PixelColor(h, s, l);
}

//...(elsewhere in code)
if(m_colorRange.size() > 0)
    m_minHSV = rgbPixelToHSV(m_colorRange[0]);
if(m_colorRange.size() > 1)
    m_maxHSV = rgbPixelToHSV(m_colorRange[m_colorRange.size() - 1]);

ShowThreshedImage(m_imgSrc,  m_minHSV, m_maxHSV);


...しかし、この変換を行わず、代わりに RGB 値を渡すだけでも、結果は完全に黒い画像になります。特定の最小値/最大値を手動で差し込むことさえ試みましたが、得られた最良の結果は、いくつかの点灯したピクセルでした (ただし、間違ったものです)。

質問:

ここで何が間違っていますか?メソッド
についてわからないことはありますか?cvInRangeSソース画像から選択した範囲を適切にしきい値処理するために、すべての色をステップスルーする必要がありますか?
これを達成する他の方法はありますか?

お時間をいただきありがとうございます。

アップデート:

がのすべての値をの値よりも低くすることをcvInRangeS期待していることを発見しました。しかし、色の範囲が選択されている場合、これが当てはまるという保証はないようで、多くの場合、黒のしきい値の画像になります。また、値を交換してこのルールを適用すると、新しい範囲内に不要な色が含まれる可能性があります (場合によっては、必要な色だけでなくすべての色が含まれる可能性があります)。minmax

したがって、ここでの本当の質問は、
「RGB カラーの配列をどのようにセグメント化し、それらを使用して画像のしきい値を設定するのですか?」ということになると思います。

4

2 に答える 2

1

あなたの問題は、OpenCV がインスタンス MSpaint とは異なる値の範囲を維持しているという単純な事実によって引き起こされる可能性があります。たとえば、ペイントの HSV 色空間は 360,100,100 ですが、OpenCV では 180,255,255 です。特定のピクセルをクリックしたときにピクセル値を出力するopenCV buの入力値を確認してください。inRangeS は、ジョブに適したツールである必要があります。そうは言っても、範囲はペイントと同じであるため、RGB でも同様に機能するはずです。

cvSetMouseCallback("MyWindow", mouseEvent, (void*) &myImage);

void mouseEvent(int evt, int x, int y, int flags, void *param) {
    if (evt == CV_EVENT_LBUTTONDOWN) {
        printf("%d %d\n", x, y);
        IplImage* imageSource = (IplImage*) param;
        Mat image(imageSource);
        cout << "Image cols " << image.cols << " rows " << image.rows << endl;
        Mat imageHSV;
        cvtColor(image, imageHSV, CV_BGR2HSV);
        Vec3b p = imageHSV.at<Vec3b > (y, x);
        char text[20];
        sprintf(text, "H=%d, S=%d, V=%d", p[0], p[1], p[2]);
        cout << text << endl;
    }
}

この値を使用して HSV 値についてのアイデアがある場合は、 を使用して画像を HSV に変換した後、これらを in range メソッドの下限および上限として使用しcvtColor(image, imageHSV, CV_BGR2HSV)ます。これにより、目的の結果を得ることができるはずです。

于 2013-05-24T08:04:54.260 に答える
0

すべてのピクセルを反復処理することは非効率的ではありません。それがまさに何cvInRangeSをするかです - これを見てください: http://docs.opencv.org/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-effective-wayサイズ画像)。

配列の色を 3D RGB 空間の点として扱います。他のすべての色点を含むプリズムを指定する 2 つの色点を見つけます。これは、すべての r、g、および b 値の最小値と最大値を見つけるだけです。この考えがうまくいかない場合は、ベクトル内のすべてのピクセルに対してすべての画像ピクセルをチェックする必要があるかもしれません。

次に、画像の各ピクセルに対して: (pixel.r < min.r) || の場合、結果は黒になります。(pixel.r > max.r) || (pixel.g < min.g) || (pixel.g > max.g) || (pixel.b < min.b) || (pixel.b > max.b)、それ以外の場合、結果はピクセル値です。

それが実際にあなたが望むものである限り、これはすべて非常に簡単なはずです.

于 2013-05-24T03:55:35.280 に答える