gocv icon indicating copy to clipboard operation
gocv copied to clipboard

How to get and set pixel color to Mat?

Open esimov opened this issue 5 years ago • 11 comments

Description

In OpenCV you can get and set pixel values in the following manner.

Mat image = img;
for(int y=0; y<img.rows; y++)
{
    for(int x=0; x<img.cols; x++)
    {
        // get pixel
        Vec3b color = image.at<Vec3b>(Point(x,y));

        // ... do something to the color ....

        // set pixel
        image.at<Vec3b>(Point(x,y)) = color;
    }
}

How can i do something similar in gocv? One way i have tried is using the SetTo method and providing a scalar as parameter, but there is no way i can tell with this method which pixel value i want to update, something like SetTo(x, y, sclarValue). Is there something i'm missing? I'm afraid iterating over the image pixels and updating the underneath matrix will update only the last iteration.

esimov avatar Oct 07 '18 18:10 esimov

I found a hacky solution but i'm not really sure if this is the way to go. First i'm converting the matrix to bytes, then i'm updating the pixel values directly on databyte level and finally creating a new Mat from bytes with gocv.NewMatFromBytes.

In code this is how it should look like:

data := mat.ToBytes()
for x := 0; x < cols; x++ {
	for y := 0; y < rows; y++ {		
		// Obtain some values from Sobel matrix.
		u := gradX.GetVecfAt(x, y)
		v := gradY.GetVecfAt(x, y)

		// Update the pixel values
		idx := x*cols*ch + y*ch
		data[idx+0] = byte(u[0])
		data[idx+1] = byte(v[0])
		data[idx+2] = 0.0
		data[idx+3] = 0.0		
	}
}
nm, err := gocv.NewMatFromBytes(rows, cols, gocv.MatChannels3, data)
if err != nil {
	log.Fatalf("Cannot create new Mat from bytes: %v", err)
}

@deadprogram Please take a look and show me what do you think?

esimov avatar Oct 09 '18 12:10 esimov

@esimov Did you found the answer for this?I also get pixel color of Mat!

ibenben avatar Oct 19 '18 11:10 ibenben

@ibenben I think the solution posted above will just work.

esimov avatar Oct 19 '18 12:10 esimov

u := gradX.GetVecfAt(x, y) Is this the way to get pixel color?Which is gradX? @esimov

ibenben avatar Oct 19 '18 12:10 ibenben

idx := xcolsch + y*ch

The index is this,I knew it.Thanks.

ibenben avatar Oct 19 '18 12:10 ibenben

@ibenben gradX is a Mat. gradX.GetVecfAt(x, y) will return a vector of floats, but if you need the pixel values you can just use GetIntAt. I'm using GetVecfAt(x, y) because of my specific needs.

esimov avatar Oct 19 '18 12:10 esimov

image

Did I do the right thing? @esimov

ibenben avatar Oct 19 '18 14:10 ibenben

You need to take care about the channel length. It might be possible that the channel length is not 3. Otherwise the code looks good, but you need to make some operations with the obtained pixels, otherwise you are only making a data byte copy.

esimov avatar Oct 19 '18 19:10 esimov

@esimov Thanks!

ibenben avatar Oct 20 '18 05:10 ibenben

Personally, I use self-writed type for uchar based on gocv.Veci approach. I think you can use same logic on any type.

type Vecb []uint8

func GetVecbAt(m gocv.Mat, row int, col int) Vecb {
	ch := m.Channels()
	v := make(Vecb, ch)

	for c := 0; c < ch; c++ {
		v[c] = m.GetUCharAt(row, col*ch+c)
	}

	return v
}

func (v Vecb) SetVecbAt(m gocv.Mat, row int, col int) {
	ch := m.Channels()

	for c := 0; c < ch; c++ {
		m.SetUCharAt(row, col*ch+c, v[c])
	}
}

Although, you shouldn't use SetVecbAt function on Mat, that has different channel count

nikolaiianchuk avatar Nov 21 '18 13:11 nikolaiianchuk

This worked for me:

// img is of type Mat
colorRed := gocv.NewScalar(0,0,255,0) // B, G, R and alpha
img.SetTo(color)

... all pixels are set to RED ... Let's draw a red 100x200 pixels rectangle (RGB image):

img := gocv.NewMatWithSize(200,100, gocv.MatTypeCV8UC3)
img.SetTo(gocv.NewScalar(0,0,255,0))
gocv.IMWrite("/tmp/rectangle.jpg",img)

budo76 avatar Aug 26 '21 13:08 budo76