I want to keep track of a point/pixel for reference in a PIL image while I do a (perspective) transformation and cut off the transparent borders.
from PIL import Image
# load image
img = Image.open("img.png")
# do some perspective transformation
img.transform(new_size, Image.PERSPECTIVE, mapping_coeffs)
# cut the borders
img = img.crop(img.getbbox())
For the cropping I could keep track of a position by subtracting the size of the padding. But how can I do this for a perspective transformation, or even multiple transformations in a row?
For others with the same question, I made a black image with only the reference pixel in white using NumPy and transformed it in the same way as my image.
from PIL import Image
import numpy as np
# get black img with the same size
refArray = np.zeros(PILimg.size)
# make the reference pixel white
refArray[xRef, yRef] = 1e8
# to PIL image object
refImg = Image.fromarray(refArray.T)
Do the same transformations with the reference image, and then find the max value in the transformed reference image
ref = np.array(refImg).T
xRef, yRef = np.unravel_index(np.argmax(ref), ref.shape)
edit: For some transformations the pixel disappears, this is solved by using a small square of pixels (5x5) instead of a single pixel.
I'm working in text extraction process inside the table.But while removing the table lines it affecting the text's pixel.is is possible to keep the text pixel which is overlays on the table line pixel.
original image as RGB
this image is the cropped from original image for reference
output region
Use eroded (or dilated black objects) second image as mask for first image.
import cv2
import numpy as np
#images need equal size
original=cv2.imread('RdfpD.png')
mask = cv2.imread('zxLX4.png', cv2.IMREAD_GRAYSCALE)
se=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,5))
ret,thresh = cv2.threshold(mask,60,255,cv2.THRESH_BINARY_INV)
dilate = cv2.dilate(thresh,se,iterations = 1)
dilate=cv2.bitwise_not(dilate)
dilate=cv2.cvtColor(dilate, cv2.COLOR_GRAY2BGR)
out=cv2.max(dilate, original)
cv2.imwrite('out_5.png', out)
I have an image in RGB and another segmented image in which the pixels have 3 values(segmented image). I want to overlay the segmented image on top of the main image as the segmented areas make contours over the main image such as image below. Here the value of the segmented image pixels are 0, 1 and 2. The red contour shows the contour of pixels with value1 , the yellow contour shows the contour of pixels with 2 value and the background pixel value is 0.
the image is from the paper "Dilated-Inception Net: Multi-Scale FeatureAggregation for Cardiac Right VentricleSegmentation"
Here is an example of a segmented image.
segmented image
The background image can be any image. I only need these rectangle counters appear on the background image as two contours similar to red and yellow lines above. So, the output will be similar to the image below.
output image
sorry as I draw rectangles by hand they are not exact. I just would like to give you an insight about the output.
I had a go at this using four different methods:
OpenCV
PIL/Pillow and Numpy
command-line with ImageMagick
morphology from skimage
Method 1 - OpenCV
Open segmented image as greyscale
Open main image as greyscale and make colour to allow annotation
Find the contours using cv2.findContours()
Iterate over contours and use cv2.drawContours() to draw each one onto main image in colour according to label in segmented image.
Documentation is here.
So, starting with this image:
and this segmented image:
which looks like this when contrast-stretched and the sandwich is labelled as grey(1) and the snout as grey(2):
Here's the code:
#!/usr/bin/env python3
import numpy as np
import cv2
# Load images as greyscale but make main RGB so we can annotate in colour
seg = cv2.imread('segmented.png',cv2.IMREAD_GRAYSCALE)
main = cv2.imread('main.png',cv2.IMREAD_GRAYSCALE)
main = cv2.cvtColor(main,cv2.COLOR_GRAY2BGR)
# Dictionary giving RGB colour for label (segment label) - label 1 in red, label 2 in yellow
RGBforLabel = { 1:(0,0,255), 2:(0,255,255) }
# Find external contours
_,contours,_ = cv2.findContours(seg,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
# Iterate over all contours
for i,c in enumerate(contours):
# Find mean colour inside this contour by doing a masked mean
mask = np.zeros(seg.shape, np.uint8)
cv2.drawContours(mask,[c],-1,255, -1)
# DEBUG: cv2.imwrite(f"mask-{i}.png",mask)
mean,_,_,_ = cv2.mean(seg, mask=mask)
# DEBUG: print(f"i: {i}, mean: {mean}")
# Get appropriate colour for this label
label = 2 if mean > 1.0 else 1
colour = RGBforLabel.get(label)
# DEBUG: print(f"Colour: {colour}")
# Outline contour in that colour on main image, line thickness=1
cv2.drawContours(main,[c],-1,colour,1)
# Save result
cv2.imwrite('result.png',main)
Result:
Method 2 - PIL/Pillow and Numpy
Open segmented image and find unique colours
Open main image and desaturate
Iterate over each unique colour in list
... Make all pixels that colour white and all others black
... Find edges and use edges as mask to draw colour on main image
Here's the code:
#!/usr/bin/env python3
from PIL import Image, ImageFilter
import numpy as np
def drawContour(m,s,c,RGB):
"""Draw edges of contour 'c' from segmented image 's' onto 'm' in colour 'RGB'"""
# Fill contour "c" with white, make all else black
thisContour = s.point(lambda p:p==c and 255)
# DEBUG: thisContour.save(f"interim{c}.png")
# Find edges of this contour and make into Numpy array
thisEdges = thisContour.filter(ImageFilter.FIND_EDGES)
thisEdgesN = np.array(thisEdges)
# Paint locations of found edges in color "RGB" onto "main"
m[np.nonzero(thisEdgesN)] = RGB
return m
# Load segmented image as greyscale
seg = Image.open('segmented.png').convert('L')
# Load main image - desaturate and revert to RGB so we can draw on it in colour
main = Image.open('main.png').convert('L').convert('RGB')
mainN = np.array(main)
mainN = drawContour(mainN,seg,1,(255,0,0)) # draw contour 1 in red
mainN = drawContour(mainN,seg,2,(255,255,0)) # draw contour 2 in yellow
# Save result
Image.fromarray(mainN).save('result.png')
You'll get this result:
Method 3 - ImageMagick
You can also do the same thing from the command-line without writing any Python, and just using ImageMagick which is installed on most Linux distros and is available for macOS and Windows:
#!/bin/bash
# Make red overlay for "1" labels
convert segmented.png -colorspace gray -fill black +opaque "gray(1)" -fill white -opaque "gray(1)" -edge 1 -transparent black -fill red -colorize 100% m1.gif
# Make yellow overlay for "2" labels
convert segmented.png -colorspace gray -fill black +opaque "gray(2)" -fill white -opaque "gray(2)" -edge 1 -transparent black -fill yellow -colorize 100% m2.gif
# Overlay both "m1.gif" and "m2.gif" onto main image
convert main.png -colorspace gray -colorspace rgb m1.gif -composite m2.gif -composite result.png
Method 4 - Morphology from skimage
Here I am using morphology to find black pixels near 1 pixels and black pixels near 2 pixels.
#!/usr/bin/env python3
import skimage.filters.rank
import skimage.morphology
import numpy as np
import cv2
# Load images as greyscale but make main RGB so we can annotate in colour
seg = cv2.imread('segmented.png',cv2.IMREAD_GRAYSCALE)
main = cv2.imread('main.png',cv2.IMREAD_GRAYSCALE)
main = cv2.cvtColor(main,cv2.COLOR_GRAY2BGR)
# Create structuring element that defines the neighbourhood for morphology
selem = skimage.morphology.disk(1)
# Mask for edges of segment 1 and segment 2
# We are basically looking for pixels with value 1 in the segmented image within a radius of 1 pixel of a black pixel...
# ... then the same again but for pixels with a vaue of 2 in the segmented image within a radius of 1 pixel of a black pixel
seg1 = (skimage.filters.rank.minimum(seg,selem) == 0) & (skimage.filters.rank.maximum(seg, selem) == 1)
seg2 = (skimage.filters.rank.minimum(seg,selem) == 0) & (skimage.filters.rank.maximum(seg, selem) == 2)
main[seg1,:] = np.asarray([0, 0, 255]) # Make segment 1 pixels red in main image
main[seg2,:] = np.asarray([0, 255, 255]) # Make segment 2 pixels yellow in main image
# Save result
cv2.imwrite('result.png',main)
Note: JPEG is lossy - do not save your segmented image as JPEG, use PNG or GIF!
Keywords: Python, PIL, Pillow, OpenCV, segmentation, segmented, labelled, image, image processing, edges, contours, skimage, ImageMagick, scikit-image, morphology, rank, ranking filter, pixel adjacency.
These are quick one-liners that automatically choose colors for the category/class integer values and execute the overlay onto the original image.
Color entire segmentation area:
from skimage import color
result_image = color.label2rgb(segmentation_results, input_image)
Color contours of segmentation areas:
from skimage import segmentation
result_image = segmentation.mark_boundaries(input_image, segmentation_results, mode='thick')
If semi-transparent segmentation masks are to be displayed on top of the image, skimage has a built-in label2rgb() function that colorizes by a label channel:
Input Image
from skimage import io, color
import matplotlib.pyplot as plt
import numpy as np
seg = np.zeros((256,256)) # create a matrix of zeroes of same size as image
seg[gt > 0.95] = 1 # Change zeroes to label "1" as per your condition(s)
seg[zz == 255] = 2
io.imshow(color.label2rgb(seg,img,colors=[(255,0,0),(0,0,255)],alpha=0.01, bg_label=0, bg_color=None))
plt.show()
I found something reasonably close to what I want to do here:
Python: PIL replace a single RGBA color
However, in my scenario I have images that were originally grayscale with color annotations added to the image (an x-ray with notes in color). I would like to replace any pixel that is not grayscale with random noise. My main problem is replacing values with noise and not a single color.
Edit: I figured out the random noise part, now just trying to figure out how to separate the color pixels from the pixels that were originally in grayscale.
from PIL import Image
import numpy as np
im = Image.open('test.jpg')
data = np.array(im) # "data" is a height x width x 3 numpy array
red, green, blue = data.T # Temporarily unpack the bands for readability
# Replace white with random noise...
white_areas = (red == 255) & (blue == 255) & (green == 255)
Z = random.random(data[...][white_areas.T].shape)
data[...][white_areas.T] = Z
im2 = Image.fromarray(data)
im2.show()
You could try
col_areas = np.logical_or(np.not_equal(red, blue), np.not_equal(red, green))
You could use this Pixel Editing python module
from PixelMenu import ChangePixels as cp
im = Image.open('test.jpg')
grayscalergb=(128, 128, 128) #RGB value of gray in your image
noise=(100,30,5) #You can adjust the noise based upon your requirements
outputimg=cp(im, col=grayscalergb, col2=noise, save=False,tolerance=100) #Adjust the tolerance until you get the right amount of noise in your image
Also:
I'd suggest you to use png images instead of jpg images because JPEG is designed with compression, everytime you load the image the RGB values change making it hard for your code to function perfectly everytime
I am working on hair removal from skin lesion images. Is there any way to convert binary back to rgb?
Original Image:
Mask Image:
I just want to restore the black area with the original image.
As I know binary images are stored in grayscale in opencv values 1-->255.
To create „dummy“ RGB images you can do:
rgb_img = cv2.cvtColor(binary_img, cv.CV_GRAY2RGB)
I call them „dummy“ since in these images the red, green and blue values are just the same.
Something like this, but your mask is the wrong size (200x200 px) so it doesn't match your image (600x450 px):
#!/usr/local/bin/python3
from PIL import Image
import numpy as np
# Open the input image as numpy array
npImage=np.array(Image.open("image.jpg"))
# Open the mask image as numpy array
npMask=np.array(Image.open("mask2.jpg").convert("RGB"))
# Make a binary array identifying where the mask is black
cond = npMask<128
# Select image or mask according to condition array
pixels=np.where(cond, npImage, npMask)
# Save resulting image
result=Image.fromarray(pixels)
result.save('result.png')
I updated the Daniel Tremer's answer:
import cv2
opencv_rgb_img = cv2.cvtColor(opencv_image, cv2.COLOR_GRAY2RGB)
opencv_image would be two dimension matrix like [width, height] because of binary.
opencv_rgb_img would be three dimension matrix like [width, height, color channel] because of RGB.