I would like to reconstruct and display an image from its superpixel representation.
Let's illustrate it with a simple example :
l have a (224,224,3) image. I apply on it superpixel SLIC algorithm to get superpixels.
Here is my code :
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
def sp_idx(s, index=True):
u = np.unique(s)
return [np.where(s == i) for i in u]
image_index=[] # store the index of pixels affected to each superpixel
image_superpixels=[] # store the RGB pixel values of the pixels of all superpixels
img = skimageIO.imread(image_1)
segments_slic = slic(img, n_segments=1000, compactness=0.01, sigma=1)
superpixel_list = sp_idx(segments_slic)# get pixel and superpixel index
image_index.append(superpixel_list)
superpixel = [img[idx] for idx in superpixel_list]
superpixel = np.asarray(superpixel)
image_superpixels.append(superpixel)
Now given the superpixels of the image :
How can display the image in superpixel format ?
l would like to get something like the following :
`plt.imshow(image_1)` and `plt.imshow(image_superpixels)`
display the same thing (at least visually comparable)
Related
I want to convert a set of point cloud (X, Y, Z) to a binary mask image using python. The problem is that these points are float and out of range of 0-255. To more specific, the points are related to an object (rectangle or ellipsoid), so I should make a binary image based on Z dimension, to specify the rectangle, for example, as 0 number and other points as 1 number in binary mask.
Can anyone give me some ideas to achieve my goal?
My point is like this array:
[[-1.56675167e+01 1.59539632e+01 1.15432026e-02]
[-1.26066835e+01 6.48645007e+00 1.15510724e-02]
[-1.18854252e+01 1.71767061e+01 1.15392632e-02]
...
[-1.45721083e+01 1.39116935e+01 -9.86438582e-04]
[-1.42607847e+01 1.28141373e+01 -1.73514791e-03]
[-1.48834319e+01 1.50092497e+01 7.59929187e-04]]
I was tried to get such binary mask that was answered in this example ():
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.path import Path
from descartes import PolygonPatch
import alphashape
from shapely.geometry import Point, Polygon
def poly2mask():
# First of all, I separated the contour of the polygon and get vertices
# in the border
hull = alphashape.alphashape(surface_org, 0.) # convex hull
poly = PolygonPatch(hull, alpha=0.2, edgecolor='#999999')
vertices = poly.get_path().vertices
x = vertices[:, 0] * 10
y = vertices[:, 1] * 10
vertices_ls = list(zip(x, y))
width, height = 120, 120
poly_path = Path(vertices_ls, closed=True)
x, y = np.mgrid[:height, :width]
coors = np.hstack((x.reshape(-1, 1), y.reshape(-1, 1)))
mask = poly_path.contains_points(coors)
mask = mask.reshape(height, width)
#print(mask)
plt.imshow(mask)
plt.ylim(-200, 200)
plt.xlim(-200, 200)
plt.show()
The image would look like this:
enter image description here
Following this example of K means clustering I want to recreate the same - only I'm very keen for the final image to contain just the quantized colours (+ white background). As it is, the colour bars get smooshed together to create a pixel line of blended colours.
Whilst they look very similar, the image (top half) is what I've got from CV2 it contains 38 colours total.
The lower image only has 10 colours and is what I'm after.
Let's look at a bit of that with 6 times magnification:
I've tried :
# OpenCV and Python K-Means Color Clustering
# build a histogram of clusters and then create a figure
# representing the number of pixels labeled to each color
hist = colour_utils.centroid_histogram(clt)
bar = colour_utils.plot_colors(hist, clt.cluster_centers_)
bar = cv2.resize(bar, (460, 345), 0, 0, interpolation = cv2.INTER_NEAREST)
However, the resize seems to have no resizing effect or change the scaling type. I don't know what controls the initial image size either.
Confused.
Any ideas?
I recommend you to show the image using cv2.imshow, instead of using matplotlib.
cv2.imshow shows the image "pixel to pixel" by default, while matplotlib.pyplot matches the image dimensions to the size of the axes.
bar_bgr = cv2.cvtColor(bar, cv2.COLOR_RGB2BGR) # Convert RGB to BGR
cv2.imshow('bar', bar_bgr)
cv2.waitKey()
cv2.destroyAllWindows()
In case you want to use matplotlib, take a look at: Display image with a zoom = 1 with Matplotlib imshow() (how to?).
Code used for testing:
# import the necessary packages
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import argparse
#import utils
import cv2
def centroid_histogram(clt):
# grab the number of different clusters and create a histogram
# based on the number of pixels assigned to each cluster
numLabels = np.arange(0, len(np.unique(clt.labels_)) + 1)
(hist, _) = np.histogram(clt.labels_, bins = numLabels)
# normalize the histogram, such that it sums to one
hist = hist.astype("float")
hist /= hist.sum()
# return the histogram
return hist
def plot_colors(hist, centroids):
# initialize the bar chart representing the relative frequency
# of each of the colors
bar = np.zeros((50, 300, 3), dtype = "uint8")
startX = 0
# loop over the percentage of each cluster and the color of
# each cluster
for (percent, color) in zip(hist, centroids):
# plot the relative percentage of each cluster
endX = startX + (percent * 300)
cv2.rectangle(bar, (int(startX), 0), (int(endX), 50),
color.astype("uint8").tolist(), -1)
startX = endX
# return the bar chart
return bar
# load the image and convert it from BGR to RGB so that
# we can dispaly it with matplotlib
image = cv2.imread('chelsea.png')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# show our image
plt.figure()
plt.axis("off")
plt.imshow(image)
# reshape the image to be a list of pixels
image = image.reshape((image.shape[0] * image.shape[1], 3))
# cluster the pixel intensities
clt = KMeans(n_clusters = 5)
clt.fit(image)
# build a histogram of clusters and then create a figure
# representing the number of pixels labeled to each color
hist = centroid_histogram(clt)
bar = plot_colors(hist, clt.cluster_centers_)
# show our color bart
#plt.figure()
#plt.axis("off")
#plt.imshow(bar)
#plt.show()
bar = cv2.resize(bar, (460, 345), 0, 0, interpolation = cv2.INTER_NEAREST)
bar_bgr = cv2.cvtColor(bar, cv2.COLOR_RGB2BGR) # Convert RGB to BGR
cv2.imshow('bar', bar_bgr)
cv2.waitKey()
cv2.destroyAllWindows()
I have the following NumPy array of a running man, which you can download here:
https://drive.google.com/file/d/1SfIEqGsBV_vA7iP4UjLdklLJlLdDzozL/view?usp=sharing
To display it, use this code:
import numpy as np
import matplotlib.pyplot as plt
# load data
data = np.load('running_man.npy')
# plot data
plt.imshow(data)
As you can see there is a lot of noise (freckles) in the image. I would like to get rid of it and retrieve a clean image of the runner. Any idea of how to do it?
This is what I have done so far:
from skimage import measure
# Find contours at a constant value of 1
contours = measure.find_contours(data, 1, fully_connected='high')
# Select the largest contiguous contour
contour = sorted(contours, key=lambda x: len(x))[-1]
# Create an empty image to store the masked array
r_mask = np.zeros_like(data, dtype='bool')
# Create a contour image by using the contour coordinates rounded to their nearest integer value
r_mask[np.round(contour[:, 0]).astype('int'), np.round(contour[:, 1]).astype('int')] = 1
# Fill in the hole created by the contour boundary
r_mask = ndimage.binary_fill_holes(r_mask)
# Invert the mask since one wants pixels outside of the region
r_mask = ~r_mask
plt.imshow(r_mask)
... but as you can see the outline is very rough !
What works well is to upload the image to an online jpg to SVG converter -> this makes the lines super smooth. ... but I want to be able to do it in python.
Idea:
I am looking for something that can keep the sharp corners, maybe something that detects the gradient along the edge and only keeps the point where the gradient is above a certain threshold...
For this specific image you can just use numpy:
import numpy as np
import matplotlib.pyplot as plt
data = np.load('running_man.npy')
data[data > 1] = 0
plt.xticks([])
plt.yticks([])
plt.imshow(data)
For a method that preserves the corners better, we can use median filters, but force the preservation of corners.
Masked Image
Mask after filtering
Recolored
import cv2
import numpy as np
# load image
img = cv2.imread("run.png");
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
# make mask
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU);
# median filter
med = cv2.medianBlur(thresh, 11);
med[thresh == 255] = 255;
# inverse filter
mask = cv2.bitwise_not(med);
med = cv2.medianBlur(mask, 3);
med[mask == 255] = 255;
# recolor
color = np.zeros_like(img);
color[med == 0] = (66, 239, 245);
color[med == 255] = (92, 15, 75);
# show
cv2.imshow("colored", color);
cv2.waitKey(0);
I have set of 17 images and one of them has a highlighted pixel for my use. But, when I merge these 17 images, I get the color but it diffuses out of the pixel boundaries and I start seeing some colored pixel in black background.
I am using PIL library for the merging. I am attaching my code and the images for the reference. Any help would be appreciated.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# Cretaing the Pixel array
from PIL import Image
from PIL import ImageColor
img_path = '/Volumes/MY_PASSPORT/JRF/cancer_genome/gopal_gen/png_files/'
image_list = []
for entry in os.listdir(img_path):
if entry.endswith('.png'):
entry = int(entry.rstrip('.csv.png'))
image_list.append(entry)
image_list.sort()
list_img = []
for j in range(len(image_list)):
stuff = str(image_list[j])+'.csv.png'
list_img.append(stuff)
#print(list_img[0])
images = [Image.open(img_path+x) for x in list_img]
widths, heights = zip(*(i.size for i in images))
total_width = sum(widths)
max_height = max(heights)
#print(total_width, max_height)
new_im = Image.new('RGB', (total_width, max_height))
x_offset = 0
for im in images:
new_im.paste(im, (x_offset,0))
#print(im.size)
x_offset += im.size[0]
#print(x_offset)
new_im.save(img_path+'final_result_image.jpg')
Here is the combined image:
The third column has a pixel highlighted.
Here is the zoomed in part with the problem.
The JPEG format is lossy - it is allowed to change your pixels to make the file smaller. If your image is a conventional photo of a real-life scene, this doesn't normally matter. If your data is a blocky, computer-generated image, or a set of classes from a classification process, it can go horribly wrong if you use JPEG.
So, the answer is to use PNG (or potentially TIFF) format for images that need to be lossless.
[I have the images as below, i need to extract just the white strip portion from all the images.
i Have tried using PIL to extract the rectangular portion by manually specifying the pixel value, Can there be any automated way to get this work done where by just feeding the image gives back the rectangular portion
Below is My snipped code:
from PIL import Image
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = Image.open('C:/Users/ShAgarwal/Documents/image_dataset/pic9.jpg')
half_the_width = img.size[0] / 2
half_the_height = img.size[1] / 2
img4 = img.crop(
(
half_the_width-1632,
half_the_height - 440,
half_the_width+1632,
half_the_height + 80
)
)
sample image
import cv2
import numpy as np
from matplotlib import pyplot as plt
image='IMG_3134.JPG'
# read image
imgc = cv2.imread(image)
img = cv2.resize(imgc, None, fx=0.25, fy=0.25) # resize since image is huge
#cropping the strip dimensions
#crop_img = img[1010:1650,140:1099723]
blurred = cv2.blur(img, (3,3))
canny = cv2.Canny(blurred, 50, 200)
Marking coordinates through auto image detection using canny's algorithm
## find the non-zero min-max coords of canny
pts = np.argwhere(canny>0)
y1,x1 = pts.min(axis=0)
y2,x2 = pts.max(axis=0)`
`## crop the region
cropped = img[y1:y2, x1:x2]
cv2.imwrite("cropped.png", cropped)
#Select the bounded area around white boundary
tagged = cv2.rectangle(img.copy(), (x1,y1), (x2,y2), (0,255,0), 3, cv2.LINE_AA)
r = cv2.selectROI(tagged)
imCrop = im[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
#Bounded Area
cv2.imwrite("taggd2.png", imcrop)
cv2.waitKey()
Results from above code