Cropping ROI regions in an numpy array - python

I am writing some codes to cut two seprate ROI regions in an numpy array. The array is a mask array with boolean values and it consists of two major left and right parts.
I need to crops those left and right parts from my original numpy array. My codes are as follow which are a section of function (image and masked are passed to this function)
if mask.shape[-1] > 0:
# We're treating all instances as one, so collapse the mask into one layer
mask = (np.sum(mask, -1, keepdims=True) >= 1)
zeros=np.zeros(image.shape)
#splash = np.where(mask, image, gray).astype(np.uint8)
splash = np.where(mask, image, zeros).astype(np.uint8)
I am not sure how to achieve this as I am really new to bumpy. I can splash the imge but what I need is differt I ned to crop two left and right parts and for this I need to crop or reshape the mask array. I have attached a splashed output sample to this thread

This is a very typical problem in computer vision. One popular solution to this is the Connected Component Labeling (or CCL). OpenCV has already an implementation for this:
https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5
Then you may use blob analysis to crop out the objects:
https://docs.opencv.org/3.4/d0/d7a/classcv_1_1SimpleBlobDetector.html

Related

How to get border pixels of an image in python?

I have an image, using steganography I want to save the data in border pixels only.
In other words, I want to save data only in the least significant bits(LSB) of border pixels of an image.
Is there any way to get border pixels to store data( max 15 characters text) in the border pixels?
Plz, help me out...
OBTAINING BORDER PIXELS:
Masking operations are one of many ways to obtain the border pixels of an image. The code would be as follows:
a= cv2.imread('cal1.jpg')
bw = 20 //width of border required
mask = np.ones(a.shape[:2], dtype = "uint8")
cv2.rectangle(mask, (bw,bw),(a.shape[1]-bw,a.shape[0]-bw), 0, -1)
output = cv2.bitwise_and(a, a, mask = mask)
cv2.imshow('out', output)
cv2.waitKey(5000)
After I get an array of ones with the same dimension as the input image, I use cv2.rectangle function to draw a rectangle of zeros. The first argument is the image you want to draw on, second argument is start (x,y) point and the third argument is the end (x,y) point. Fourth argument is the color and '-1' represents the thickness of rectangle drawn (-1 fills the rectangle). You can find the documentation for the function here.
Now that we have our mask, you can use 'cv2.bitwise_and' (documentation) function to perform AND operation on the pixels. Basically what happens is, the pixels that are AND with '1' pixels in the mask, retain their pixel values. Pixels that are AND with '0' pixels in the mask are made 0. This way you will have the output as follows:
.
The input image was :
You have the border pixels now!
Using LSB planes to store your info is not a good idea. It makes sense when you think about it. A simple lossy compression would affect most of your hidden data. Saving your image as JPEG would result in loss of info or severe affected info. If you want to still try LSB, look into bit-plane slicing. Through bit-plane slicing, you basically obtain bit planes (from MSB to LSB) of the image. (image from researchgate.net)
I have done it in Matlab and not quite sure about doing it in python. In Matlab,
the function, 'bitget(image, 1)', returns the LSB of the image. I found a question on bit-plane slicing using python here. Though unanswered, you might want to look into the posted code.
To access border pixel and enter data into it.
A shape of an image is accessed by t= img.shape. It returns a tuple of the number of rows, columns, and channels.A component is RGB which 1,2,3 respectively.int(r[0]) is variable in which a value is stored.
import cv2
img = cv2.imread('xyz.png')
t = img.shape
print(t)
component = 2
img.itemset((0,0,component),int(r[0]))
img.itemset((0,t[1]-1,component),int(r[1]))
img.itemset((t[0]-1,0,component),int(r[2]))
img.itemset((t[0]-1,t[1]-1,component),int(r[3]))
print(img.item(0,0,component))
print(img.item(0,t[1]-1,component))
print(img.item(t[0]-1,0,component))
print(img.item(t[0]-1,t[1]-1,component))
cv2.imwrite('output.png',img)

Python Pillow: how to overlay one binary image on top of another to produce a composite?

I am doing some image processing, and I need to check if a binary image is identical to another.
Processing speed isn't an issue, and the simple thing I thought to do was count the white pixels remaining after adding the inverse of image A to image B (these images are very nearly identical, but not quite--some sort of distance metric is the goal).
Note: take the logarithm to linearize the distance
However, in order to create the composite image, I need to include a "mask" that is the same size as the two images.
I am having trouble finding an example of creating the mask online and using it for the Image.composite function.
Here is my code:
compA = ImageOps.invert(imgA)
imgAB = Image.composite(compA,imgB,??? mask)
Right now, I have created a mask of all zeros--however, the composite image does not appear correctly (both A and B are exactly the same images; a mask of all zeros--or all ones for that matter--does not work).
mask = Image.fromarray(np.zeros(imgA.size,dtype=int),mode='L')
imgAB = Image.composite(compA,imgB,mask)
How do I just add these two binary images on top of eachother?
Clearly you're using numpy, so why not just work with numpy arrays and explicitly do whatever arithmetic you want to do in that domain—such as subtracting one image from the other:
arrayA = numpy.asarray( imgA, dtype=int )
arrayB = numpy.asarray( imgB, dtype=int )
arrayDelta = arrayA - arrayB
print( (arrayDelta !=0 ).sum() ) # print the number of non-identical pixels (why count them by hand?)
# NB: this number may be inflated by a factor of 3 if there are 3 identical channels R, G, B
imgDelta = Image.fromarray((numpy.sign(arrayDelta)*127+127).astype('uint8')) # display this image if you want to visualize where the differences are
You could do this even more simply, e.g.
print((numpy.asarray(imgA) != numpy.asarray(imgB)).sum())
but I thought casting to a signed integer type first and then subtracting would allow you to visualize more information (A white and B black -> white pixel in delta; A black and B white -> black pixel in delta)

Using OpenCV Python, How would you make all black pixels transparent, and then overlay it over original image

I'm trying to make a colored mask, white.
And my idea is to:
make black pixels transparent in the mask
merge the two images
crop images
so then my original masked area will be white.
What kind of OpenCV python code/methods would I need?
Like so:
Original
Mask
Desired result (mocked up - no green edges)
Instead of
I suppose to do a color threshold to get the mask itself.
The result I got in a first quick and dirty attempt with Hue 43-81, Saturation 39-197 and Brightness from 115-255 is:
The next step is a whole fill algorithm to fill the inside of the mask. Note that also one small area to the right is selected.
The next step is a substraction of the two results (mask-filled_mask):
Again fill the wholes and get rid of the noisy pixels with binary opening:
Last mask the image with the created mask.
Every step can be adjusted to yield optimal results. A good idea is to try the steps out (for example with imageJ) to get your workflow set up and then script the steps in python/openCV.
Refer also to http://fiji.sc/Segmentation.
I am assuming your mask is a boolean numpy array and your 2 images are numpy arrays image1 and image2.
Then you can use the boolean array as multiplier.
overlay= mask*image1 + (-mask)*image2
So you get the "True" pixels from image1 and the False pixels from image2

Correlating two skeletized images : Python

import cv2
import numpy as np
from PIL import Image
from skimage import morphology
from scipy import signal
img = cv2.imread('thin.jpg',0)
img1 = cv2.imread('thin1.jpg',0)
cv2.imshow('image1',img)
cv2.imshow('image2',img1)
ret,img = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
ret,img1 = cv2.threshold(img1,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
size = np.size(img)
size1 = np.size(img1)
skel = np.zeros(img.shape,np.uint8)
skel1 = np.zeros(img1.shape,np.uint8)
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
img = 255 - img
img1 = 255 - img1
img = cv2.dilate(img,element,iterations=8)
img1 = cv2.dilate(img1,element,iterations=8)
done = False
while(not done):
eroded = cv2.erode(img,element)
eroded1 = cv2.erode(img1,element)
temp = cv2.dilate(eroded,element)
temp1 = cv2.dilate(eroded1,element)
temp = cv2.subtract(img,temp)
temp1 = cv2.subtract(img1,temp1)
skel = cv2.bitwise_or(skel,temp)
skel1 = cv2.bitwise_or(skel1,temp1)
img = eroded.copy()
img1 = eroded1.copy()
zeros = size - cv2.countNonZero(img)
if zeros==size:
done = True
cv2.imshow('IMAGE',skel)
cv2.imshow('TEMPLATE',skel1)
cv2.imwrite("image.jpg",skel)
if cv2.waitKey(0) & 0xFF == ord('q'):
cv2.destroyAllWindows()
This is the code that i tried to convert two grayscale image to two skeletized image using the method of binarization and thinning and the result is also obtained. Now with these two skeletized image , i want to do a comparison to see whether they match or not. How can i correlate each other? Do we need to convert this skeletized into 2d array? Can anyone suggest any solution. Thanks in advance.
There are a number of ways you can compare the images to see if they match. The simplest is to do a pixelwise subtraction to create a new image and then sum the pixels in the new image. If they sum to zero you have an exact match. The larger the sum the worse the match.
You will however have a problem using most comparison techniques on a skeletonized image. You take the image and reduce it to skinny little lines that are unlikely to overlap for images that only deviate from each other by a little bit.
With skeletonized images you often need to compare features. For example, identify the points of intersection of the skeleton, and use the location of those points for comparing images. In your sample image you might be able to extract the lines (I see three major ones) and then compare images based on the location of the lines.
Binary images are already represented as 2D numpy arrays.
This is a complex problem. You can do this by reshaping the images to two vectors (assuming they are exactly the same size), and then calculating the correlation coefficient:
np.corrcoef(img.reshape(-1), img1.reshape(-1))
One possible solution would be to correlate (or subtract) the blurred version of each skeletonized image with one another.
That way, the unavoidable little offsets between skeleton lines wouldn't have such a negative impact on the outcome as if you subtracted the skeletons directly (since the skeleton lines would most probably not overlay exactly over one another).
I'm assuming here that the original images weren't similar to each other in the first place, otherwise you wouldn't need to skeletonize them, right?

Scipy Binary Closing - Edge Pixels lose value

I am attempting to fill holes in a binary image. The image is rather large so I have broken it into chunks for processing.
When I use the scipy.ndimage.morphology.binary_fill_holes functions, it fills larger holes that belong in the image. So I tried using scipy.ndimage.morphology.binary_closing, which gave the desired results of filling small holes in the image. However, when I put the chunks back together, to create the entire image, I end up with seamlines because the binary_closing function removes any values from the border pixels of each chunk.
Is there any way to avoid this effect?
Yes.
Label your image using ndimage.label (first invert the image, holes=black).
Find the hole object slices with ndimage.find_objects
Filter the list of object slices based on your size criteria
Invert back your image and perform binary_fill_holes on the slices that meet your criteria.
That should do it, without needing to chop the image up. For example:
Input image:
Output image (Middle size holes are gone):
Here is the code (inequality is set to remove the middle size blobs):
import scipy
from scipy import ndimage
import numpy as np
im = scipy.misc.imread('cheese.png',flatten=1)
invert_im = np.where(im == 0, 1, 0)
label_im, num = ndimage.label(invert_im)
holes = ndimage.find_objects(label_im)
small_holes = [hole for hole in holes if 500 < im[hole].size < 1000]
for hole in small_holes:
a,b,c,d = (max(hole[0].start-1,0),
min(hole[0].stop+1,im.shape[0]-1),
max(hole[1].start-1,0),
min(hole[1].stop+1,im.shape[1]-1))
im[a:b,c:d] = scipy.ndimage.morphology.binary_fill_holes(im[a:b,c:d]).astype(int)*255
Also note that I had to increase the size of the slices so that the holes would have border all the way around.
Operations that involve information from neighboring pixels, such as closing will always have trouble at the edges. In your case, this is very easy to get around: just process subimages that are slightly larger than your tiling, and keep the good parts when stitching together.

Categories