web-dev-qa-db-ja.com

opencvでピクセルを切り替える

Opencvを使用して2次元配列のように画像を循環させて、各ピクセルのRGB値を取得するにはどうすればよいですか?また、この操作にはマットがiplimageよりも望ましいでしょうか?

25
a sandwhich

C++を使用する場合は、opencvのC++インターフェイスを使用すると、 http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-efficientからメンバーにアクセスできます。 -way または、たとえばcv :: Mat :: at()を使用します。

10
etarion

cv::Matはコードを簡素化するため、IplImageよりも優先されます

cv::Mat img = cv::imread("lenna.png");
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++) 
        // You can now access the pixel value with cv::Vec3b
        std::cout << img.at<cv::Vec3b>(i,j)[0] << " " << img.at<cv::Vec3b>(i,j)[1] << " " << img.at<cv::Vec3b>(i,j)[2] << std::endl;

これは、RGB値を一緒に使用する必要があることを前提としています。そうでない場合は、cv :: splitを使用して各チャネルを個別に取得できます。例のリンクについては、etarionの回答を参照してください。

また、私の場合、グレースケールの画像が必要です。次に、画像をグレースケールでロードし、ucharの配列としてアクセスできます。

cv::Mat img = cv::imread("lenna.png",0);
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++)
        std::cout << img.at<uchar>(i,j) << std::endl;

[〜#〜] update [〜#〜]:スプリットを使用して3つのチャンネルを取得する

cv::Mat img = cv::imread("lenna.png");
std::vector<cv::Mat> three_channels = cv::split(img);

// Now I can access each channel separately
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++)
        std::cout << three_channels[0].at<uchar>(i,j) << " " << three_channels[1].at<uchar>(i,j) << " " << three_channels[2].at<uchar>(i,j) << std::endl;

// Similarly for the other two channels

更新:cv :: Vec3bの例からコピーアンドペーストするときに導入したエラーを見つけてくれたentarionに感謝します。

43
Dat Chu

OpenCV 3.0以降、cv :: Matのピクセル全体で機能を実行する公式かつ最速の方法があります。

void cv :: Mat :: forEach(const Functor&operation)

この関数を使用すると、操作はマルチコアで自動的に実行されます。

開示:この機能の寄稿者です。

13
user3188838

OpenCV 3.3( changelog を参照)以降、C++ 11スタイルのループを使用することもできます。

// Example 1
Mat_<Vec3b> img = imread("lena.jpg");
for( auto& pixel: img ) {
    pixel[0] = gamma_lut[pixel[0]];
    pixel[1] = gamma_lut[pixel[1]];
    pixel[2] = gamma_lut[pixel[2]];
}

// Example 2
Mat_<float> img2 = imread("float_image.exr", cv::IMREAD_UNCHANGED);
for(auto& p : img2) p *= 2;
3
Martin R.

ドキュメントは、マット画像を反復処理するさまざまな方法のよく書かれた比較を示しています ここ

最速の方法は、Cスタイルのポインターを使用することです。ドキュメントからコピーされたコードは次のとおりです。

Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));

int channels = I.channels();

int nRows = I.rows;
int nCols = I.cols * channels;

if (I.isContinuous())
{
    nCols *= nRows;
    nRows = 1;
}

int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
    p = I.ptr<uchar>(i);
    for ( j = 0; j < nCols; ++j)
    {
        p[j] = table[p[j]];
    }
}
return I;
}

Atを使用して要素にアクセスするのは非常に遅いです。

ルックアップテーブルを使用して操作を実行できる場合、組み込み関数LUTははるかに高速です(ドキュメントにも記載されています)。

3
vipers36

RGBピクセルを1つずつ変更したい場合、以下の例が役立ちます!

void LoopPixels(cv::Mat &img) {
    // Accept only char type matrices
    CV_Assert(img.depth() == CV_8U);

    // Get the channel count (3 = rgb, 4 = rgba, etc.)
    const int channels = img.channels();
    switch (channels) {
    case 1:
    {
        // Single colour
        cv::MatIterator_<uchar> it, end;
        for (it = img.begin<uchar>(), end = img.end<uchar>(); it != end; ++it)
            *it = 255;
        break;
    }
    case 3:
    {
        // RGB Color
        cv::MatIterator_<cv::Vec3b> it, end;
        for (it = img.begin<cv::Vec3b>(), end = img.end<cv::Vec3b>(); it != end; ++it) {
            uchar &r = (*it)[2];
            uchar &g = (*it)[1];
            uchar &b = (*it)[0];
            // Modify r, g, b values
            // E.g. r = 255; g = 0; b = 0;
        }
        break;
    }
    }
}
1
Acidic

これは古い質問ですが、opencvが積極的に開発されているため、更新する必要があります。最近、OpenCVはc ++ 11ラムダ関数に準拠するparallel_for_を導入しました。以下に例を示します

parallel_for_(Range(0 , img.rows * img.cols), [&](const Range& range){
    for(int r = range.start; r<range.end; r++ )
    {
         int i = r / img.cols;
         int j = r % img.cols;
        img.ptr<uchar>(i)[j] = doSomethingWithPixel(img.at<uchar>(i,j));
    }
});

これは、この方法が最新のコンピューターアーキテクチャでCPUコアを使用していることは言及に値します。

0
Davood Falahati