Zugriff auf bestimmte Pixel-RGB-Werte in openCV

78

Ich habe das Internet und den Stackoverflow gründlich durchsucht, aber keine Antwort auf meine Frage gefunden:

Wie kann ich (beide) RGB-Werte bestimmter (durch x, y-Koordinaten gegebener) Pixel in OpenCV abrufen / einstellen? Was wichtig ist - ich schreibe in C ++, das Bild wird in der Variablen cv :: Mat gespeichert. Ich weiß, dass es einen IplImage () -Operator gibt, aber IplImage ist nicht sehr benutzerfreundlich - soweit ich weiß, stammt es von der C-API.

Ja, mir ist bekannt, dass es diesen Pixelzugriff bereits im OpenCV 2.2- Thread gab, aber es ging nur um Schwarz-Weiß-Bitmaps.

BEARBEITEN:

Vielen Dank für all Ihre Antworten. Ich sehe, dass es viele Möglichkeiten gibt, den RGB-Wert eines Pixels zu erhalten / einzustellen. Ich habe noch eine Idee von meinem engen Freund - danke Benny! Es ist sehr einfach und effektiv. Ich denke, es ist Geschmackssache, welche Sie wählen.

Mat image;

(...)

Point3_<uchar>* p = image.ptr<Point3_<uchar> >(y,x);

Und dann können Sie RGB-Werte lesen / schreiben mit:

p->x //B
p->y //G
p->z //R
Wookie88
quelle

Antworten:

98

Versuche Folgendes:

cv::Mat image = ...do some stuff...;

image.at<cv::Vec3b>(y,x); gibt Ihnen den RGB-Vektor (möglicherweise als BGR bestellt) vom Typ cv::Vec3b

image.at<cv::Vec3b>(y,x)[0] = newval[0];
image.at<cv::Vec3b>(y,x)[1] = newval[1];
image.at<cv::Vec3b>(y,x)[2] = newval[2];
Boas
quelle
Können Sie mir dasselbe mit Ipl Image vorschlagen?
Frankenstein
1
Sicher! Überprüfen Sie cvGet2D docs.opencv.org/modules/core/doc/…
Boaz
Können Sie mir helfen, dasselbe mit IplImage zu schreiben?
Frankenstein
@ Frankenstein Siehe meinen Kommentar zu cvGet2D
Boaz
2
@ Ahmad typedef Vec <uchar, 3> Vec3b; - was bedeutet, dass ein Vektor von 3 Uchars (0-255), jede Komponente des RGB (das R, das G und das B) mit einem numerischen Wert zwischen 0 und 255 dargestellt wird. Die "at" -Methode des Bildes ist a Methode von cv :: Mat, einer Klasse, die eine zweidimensionale Matrix darstellt. Wenn Sie cv :: Mat mit dem Zelltyp CV_8UC3 sagen, bedeutet dies, dass jede Zelle in der Matrix in 3 Kanäle mit jeweils 8 Bit passt. Wenn Sie image.at <cv :: Vec3b) (y, x) sagen, sagen wir tatsächlich - " Holen Sie sich die Zelle X, Y in der Matrixmatte als Vektor von 3 Uchars "Jede Vektorzelle repräsentiert einen der Kanäle
Boaz
18

Der einfache Weg wäre, direkt auf die Matrixdaten zuzugreifen. In einem RGB-Bild (von dem ich glaube, dass OpenCV es normalerweise als BGR speichert) und unter der Annahme, dass Ihre Variable cv :: Mat aufgerufen wird frame, können Sie den blauen Wert an Position ( x, y) (von oben links) folgendermaßen erhalten:

frame.data[frame.channels()*(frame.cols*y + x)];

Um B, G und R zu erhalten:

uchar b = frame.data[frame.channels()*(frame.cols*y + x) + 0];    
uchar g = frame.data[frame.channels()*(frame.cols*y + x) + 1];
uchar r = frame.data[frame.channels()*(frame.cols*y + x) + 2];

Beachten Sie, dass dieser Code davon ausgeht, dass der Schritt der Breite des Bildes entspricht.

Erdferkel
quelle
Danke, es funktioniert beim Lesen des RGB-Werts. Ich habe versucht, frame.data [...] ohne Glück auf bestimmte Werte zu setzen, aber die von Boaz.Jan gegebene Lösung funktioniert.
Wookie88
@aaedvarkk Erstaunlich, du hast meinen Tag gerettet!
Jumabek Alikhanov
2
es sollte nicht sein frame.data[frame.channels()*(frame.cols*y + x)];?
Shmil The Cat
2

Ein Stück Code ist für Leute, die ein solches Problem haben, einfacher. Ich teile meinen Code und Sie können ihn direkt verwenden. Bitte beachten Sie, dass OpenCV Pixel als BGR speichert.

cv::Mat vImage_; 

if(src_)
{
    cv::Vec3f vec_;

    for(int i = 0; i < vHeight_; i++)
        for(int j = 0; j < vWidth_; j++)
        {
            vec_ = cv::Vec3f((*src_)[0]/255.0, (*src_)[1]/255.0, (*src_)[2]/255.0);//Please note that OpenCV store pixels as BGR.

            vImage_.at<cv::Vec3f>(vHeight_-1-i, j) = vec_;

            ++src_;
        }
}

if(! vImage_.data ) // Check for invalid input
    printf("failed to read image by OpenCV.");
else
{
    cv::namedWindow( windowName_, CV_WINDOW_AUTOSIZE);
    cv::imshow( windowName_, vImage_); // Show the image.
}
ShannonLiu
quelle
0

Die aktuelle Version ermöglicht es der cv::Mat::atFunktion, 3 Dimensionen zu verarbeiten . Also für ein MatObjekt m, m.at<uchar>(0,0,0)sollte funktionieren.

Jakob
quelle
5
Eigentlich atfunktioniert diese Version von nicht auf Mehrkanal-Mat. Trotz der Tatsache, dass die zweidimensionale Mehrkanal-Matte das gleiche Speicherlayout wie die dreidimensionale einkanalige Matte hat, löst die Methode aufgrund der Unterschiede im Mat-Header eine Ausnahme aus.
Andrey Kamaev
0
uchar * value = img2.data; //Pointer to the first pixel data ,it's return array in all values 
int r = 2;
for (size_t i = 0; i < img2.cols* (img2.rows * img2.channels()); i++)
{

        if (r > 2) r = 0;

        if (r == 0) value[i] = 0;
        if (r == 1)value[i] =  0;
        if (r == 2)value[i] = 255;

        r++;
}
Saef Mythos
quelle
-5
const double pi = boost::math::constants::pi<double>();

cv::Mat distance2ellipse(cv::Mat image, cv::RotatedRect ellipse){
    float distance = 2.0f;
    float angle = ellipse.angle;
    cv::Point ellipse_center = ellipse.center;
    float major_axis = ellipse.size.width/2;
    float minor_axis = ellipse.size.height/2;
    cv::Point pixel;
    float a,b,c,d;

    for(int x = 0; x < image.cols; x++)
    {
        for(int y = 0; y < image.rows; y++) 
        {
        auto u =  cos(angle*pi/180)*(x-ellipse_center.x) + sin(angle*pi/180)*(y-ellipse_center.y);
        auto v = -sin(angle*pi/180)*(x-ellipse_center.x) + cos(angle*pi/180)*(y-ellipse_center.y);

        distance = (u/major_axis)*(u/major_axis) + (v/minor_axis)*(v/minor_axis);  

        if(distance<=1)
        {
            image.at<cv::Vec3b>(y,x)[1] = 255;
        }
      }
  }
  return image;  
}
user537723
quelle
Wie beantwortet dies die Frage?
Rayryeng