CopyTo equivalent in Python OpenCV binding?

OpenCV has a copyTo function that allows you to copy a hidden area from one Mat to another.

http://docs.opencv.org/3.1.0/d3/d63/classcv_1_1Mat.html#a4331fa88593a9a9c14c0998574695ebb

What is equivalent to this in Python bindings? I would like to copy an image area to another image using a binary mask.

+6
source share
2 answers

cv::Mat::copyTo performs one of two functions, depending on whether the output matrix has been initialized. If your output matrix is ​​not initialized, using copyTo with a mask creates a new output matrix, which is the same type as the input, and all values ​​are 0 on all channels. As soon as this happens, the image data defined by the mask will be copied and the rest of the matrix will be set to 0. If your output matrix initialized and already consists of content, copyTo copies above the pixels that are defined in the mask from the source, and leaves the pixels that were not part of the mask intact at the destination. Therefore, the replacement of pixels that are determined by the mask from the original image is copied to the output.

Since OpenCV now uses numpy to interact with the library, it is very easy to make any of these methods. To distinguish from the other answer seen in this post, the first method can be implemented by simply multiplying the mask by the image element-wise. Assuming your input is called img , and your binary mask is called mask , where I assume the mask is 2D, just do the following:

 import numpy as np import cv2 mask = ... # define mask here img = cv2.imread(...) # Define input image here # Create new image new_image = img * (mask.astype(img.dtype)) 

The above code assumes that both img and mask have the same number of channels. This is difficult if you use a color image as a source and a 2D mask, as I already suggested. Therefore, the total number of channels is 2, not 3, and therefore the above syntax will give you an error, since the sizes between them are no longer compatible. When using color images, you need to adapt. You can do this by adding a one-dimensional third dimension to the mask to use broadcast transmission.

 import numpy as np import cv2 mask = ... # define mask here img = cv2.imread(...) # Define input image here # Create new image # Case #1 - Other image is grayscale and source image is colour if len(img.shape) == 3 and len(mask.shape) != 3: new_image = img * (mask[:,:,None].astype(img.dtype)) # Case #2 - Both images are colour or grayscale elif (len(img.shape) == 3 and len(mask.shape) == 3) or \ (len(img.shape) == 1 and len(mask.shape) == 1): new_image = img * (mask.astype(img.dtype)) # Otherwise, we can't do this else: raise Exception("Incompatible input and mask dimensions") 

For the second approach, suppose we have another image called other_image , where you want to copy the content in this image defined by your mask back to the target img image. In this case, what you do first is to determine all locations in the mask that are not null using numpy.where , then use these to index or slice the image, as well as where you want to copy. We must also remember the number of channels between two images, as in the first approach:

 import numpy as np import cv2 mask = ... # define mask here img = cv2.imread(...) # Define input image here other_image = cv2.imread(...) # Define other image here locs = np.where(mask != 0) # Get the non-zero mask locations # Case #1 - Other image is grayscale and source image is colour if len(img.shape) == 3 and len(other_image.shape) != 3: img[locs[0], locs[1]] = other_image[locs[0], locs[1], None] # Case #2 - Both images are colour or grayscale elif (len(img.shape) == 3 and len(other_image.shape) == 3) or \ (len(img.shape) == 1 and len(other_image.shape) == 1): img[locs[0], locs[1]] = other_image[locs[0], locs[1]] # Otherwise, we can't do this else: raise Exception("Incompatible input and output dimensions") 

Here is an example for both approaches. I am going to use a camera image that the standard test image is observed in most image processing algorithms.

enter image description here

I also artificially made the color of the image, although it was visualized as shades of gray, but the intensities will be copied to all channels. I'm also going to define a mask, which is just the top left side of 100 x 100, and so we will create an output image that copies only this subregion:

 import numpy as np import cv2 # Define image img = cv2.imread("cameraman.png") # Define mask mask = np.zeros(img.shape, dtype=np.bool) mask[:100, :100] = True 

When you use the first method and when you show the results, we get:

enter image description here

We see that we created an output image in which the upper left 100 x 100 contains our image data and the remaining pixels are 0. This depends on the mask locations that are set to True . For the second approach, we will create another image that will be random with the same size as the input image, which spans from [0, 255] for all channels.

 # Define other image other_image = (255*np.random.rand(*img.shape)).astype(np.uint8) 

As soon as we run the code with the second approach, I get this image now:

enter image description here

As you can see, the upper left corner of the image has been updated depending on the location of the masks that are set to True .

+11
source

Note that this is exactly what you want, but for copying with masks in Python I would go with cv2.bitwise_

 new_image = cv2.bitwise_and(old_image,binary_mask) 
+2
source

Source: https://habr.com/ru/post/1013945/


All Articles