Applying k-mean algorithm to image patches - python

I want to apply k-mean algorithm to image patches, is it possible ? Think I have an image and splitted into 9 patches. Then I applied k-mean algorithm to all of them by seperately and joined them. Is it possible ? I tried to sum of 9 centroid pixel values and divide into 9 so patches length but it does not give same answer when I apply it directly image and there is so difference.
Here my code:
import cv2
import numpy
import matplotlib.pyplot as plt
import patchify
image = cv2.imread(r"image.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
#image = numpy.resize(image,(2000,2500,3)) if it is needed
patched_image = patchify.patchify(image, (250,250,3),step = 250)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.000000000000000000000002)
k = 3
all_centers = []
all_indexes = []
for i in range(0,patched_image.shape[0]):
for j in range(0,patched_image.shape[1]):
img = patched_image[i,j][0]
pixel_values = img.reshape((-1, 3))
pixel_values = numpy.float32(pixel_values)
_, labels, (centers) = cv2.kmeans(pixel_values, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
centers = numpy.uint8(centers)
all_centers.append(centers)
indexes = centers.argsort(axis = 0)[:,0]
all_indexes.append(indexes)
labels = labels.flatten()
segmented_image = centers[labels]
patched_image[i,j][0]= segmented_image.reshape(img.shape)
all_centers = numpy.array(all_centers)
all_indexes = numpy.array(all_indexes)
c = numpy.where(all_indexes == 0)
t = all_centers[c]
# t is centroid values of label 0
unpatched_image = patchify.unpatchify(patched_image, image.shape)
Here an image to use as sample.

Related

Segmenting pictures using their histograms in python

I have problem with segmenting (or clustering?) pictures using their histograms - i mean i dont know totally how can i do it. I have, lets say, 200 images and i have to group it (like people to people, buildings to buildings etc.)
If you want to make exacly same task as me there is a source of images http://wang.ist.psu.edu/docs/related/
I know how to get histograms etc, my code is below (with detailed description).
import numpy as np
import matplotlib.pyplot as plt
import cv2
# 1. Loading images from folder
path = 'image\\350.jpg'
img = plt.imread(path)
imglist=[]
for i in range(0,20):
x = np.random.randint(0,1000)
path = "image\\"+str(x)+".jpg"
# print(path)
img = plt.imread(path)
imglist.append(img)
# just testing if everything is fine
plt.figure(figsize = (20,10))
for i in range(0,20):
plt.subplot(4,5,i+1)
plt.imshow(imglist[i])
plt.xticks([])
plt.yticks([])
# 2. QUANTIZATION (FOR ONE IMAGE)
# QUANTIZATION TO 2 COLORS FOR EACH OF R, G, B --> 8 COLORS
imgq2 = np.floor(img/128)*128+64
imgq2 =imgq2.astype(int)
# QUANTIZATION TO 4 COLORS
imgq4 = np.floor(img/64)*64 + 32
imgq4 = imgq4.astype(int)
# QUANTIZATION TO 8 COLORS
imgq8 = np.floor(img/32)*32 + 16
imgq8 = imgq8.astype(int)
# TESTING
plt.figure(figsize = (20,10))
plt.subplot(1,3,1)
plt.imshow(img)
plt.title('Oryginal image')
plt.subplot(1,3,2)
plt.imshow(imgq2)
plt.title('2 values of RGB')
plt.subplot(1,3,3)
plt.imshow(imgq4)
plt.title('4 values of RGB')
# checking if this is true
imgq4.shape
imgq8.shape
print(np.unique(imgq4[:,:,:]))
print(np.unique(imgq8[:,:,:]))
imgq4 = np.floor(img/128)
imgq4 = imgq4.astype(int)
# plt.imshow(imgq4)
# plt.show()
# 3. MAKING 3-Dim (RGB) HISTOGRAM
hist = {(i,j,k): 0 for i in range(0,4) for j in range(0,4) for k in range(0,4)}
print(hist)
img = plt.imread('image\\0.jpg')
img2 = img.copy()
img2 = np.floor(img2/64).astype(int)
img2.shape
img2 = img2.reshape( img2.shape[0]*img2.shape[1] ,3)
for i in range(0,img2.shape[0]):
hist[ img2[i,0],img2[i,1],img2[i,2] ] = hist[img2[i,0],img2[i,1],img2[i,2]] + 1
print(len(hist.keys()), len(hist.values()))
# histogram values
print(hist.values())
print(img2.shape[0] == np.sum(list(hist.values())))`
I have also this file, which is making clustering on random points
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import load_digits
no_of_points = 1000
X = np.random.rand(no_of_points,2)
plt.scatter(X[:,0],X[:,1],s = 50, cmap = 'rainbow')
kmeans = KMeans(n_clusters = 5)
kmeans.fit(X)
plt.scatter(X[:,0], X[:,1], s = 50, cmap = 'rainbow', c = kmeans.labels_)
plt.scatter(kmeans.cluster_centers_[:,0],kmeans.cluster_centers_[:,1],s = 100, c = 'black', alpha = 0.5)
plt.show()
To sum up, i have 2 questions.
Is this possible to make or transform somehow histograms (or vectors) to something, which i could cluster like in my second file?
How to automatize process of making image histogram? (its made just for one image)
Thanks for help!

Why are my DALL-E Images Coming Out So Desaturated After Resizing?

I'm very new to ML image manipulation/creation, so if I confuse you all with my own lack of knowledge on the subject, I apologize in advance.
I'm attempting to increase the resolution of images produced by DALL-E, inspired by this article:
https://towardsdatascience.com/big-art-using-machine-learning-to-create-high-res-fine-art-7dd695f99788
However, when I attempt to feed images from DALL-E into the "Generate 1K Image" section of the original author's code/colab, my original image becomes very washed out; likely because in the source, some sort of tensor from a different model is fed in, while in my own version, I'm converting an image to a (poorly made?) tensor in the same section and then feeding that in.
Here's what I have:
##title Generate 1K Image
from google.colab import files
from io import BytesIO
from PIL import Image
from matplotlib import pyplot as plt
import numpy as np
from torchvision import transforms as T
import IPython
import os.path
import cv2
uploaded = files.upload()
texture_amount = 0.05 ##param {type:"slider", min:0, max:0.15, step:0.001}
texture_size = 3 ##param {type:"slider", min:1, max:9, step:2}
enhance_details = True ##param {type:"boolean"}
img = Image.open(BytesIO(uploaded['knight.png']))
plt.imshow(img)
plt.show()
transform = transforms.Compose([
transforms.ToTensor()
])
tensorImage = transform(img)
selected_img = tensorImage.cuda()
selected_img = selected_img.type(torch.cuda.FloatTensor)
selected_img = selected_img.add(1).div(2)[None, :]
with torch.no_grad():
torch.cuda.empty_cache()
resized = bsrgan_model(selected_img)
torch.cuda.empty_cache()
noise = torch.normal(0, texture_amount,
size=[resized.shape[0], 1, resized.shape[2], resized.shape[3]]).to(device)
noise = noise.repeat(1, 3, 1, 1)
noise_blurred = T.GaussianBlur(kernel_size=texture_size, sigma=1)(noise)
noise_blurred = noise*0.25 + noise_blurred*0.75
resized = (resized+noise_blurred)
final_image = resized.to(device)
if enhance_details:
with torch.no_grad():
torch.cuda.empty_cache()
z, *_ = vqgan_model.encode(final_image * 2 - 1)
final_image = vqgan_model.decode(z)[0].add(1).div(2).clamp(min=0, max=1)
torch.cuda.empty_cache()
final_image = final_image.clamp(min=0, max=1)
else:
final_image = final_image[0].clamp(min=0, max=1)
img = T.ToPILImage()(final_image)
img.save("output_1k.png")
IPython.display.Image("output_1k.png")
Original Image
Resulting Image
Any ideas as to how I can fix this issue is greatly, greatly appreciated!
Solution
You are calculating average with 1 for every pixel values in this line.
selected_img = selected_img.add(1).div(2)[None, :]
You should change it to this line
selected_img = selected_img[None, :]
Explanation
(For a pixel if R G B = 1 1 1, the color of the pixel is white, and if R G B = 0 0 0 the color of the pixel is black.) For example if for a pixel these values are R G B = 0 .5 1, in the line below, you're changing it to R G B = .5 .75 1(getting average with 1). You can check it with this code.
import torch
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
from torchvision import transforms
fig, axs = plt.subplots(nrows=1, ncols=3, constrained_layout=True)
img = Image.open('knight.png')
axs[0].imshow(img)
axs[0].set_title('Original Image')
transform = transforms.Compose([
transforms.ToTensor()
])
tensorImage = transform(img)
selected_img = tensorImage
selected_img = selected_img.type(torch.FloatTensor)
selected_img_0 = selected_img.add(1).div(2)[None, :]
axs[1].imshow(np.squeeze(selected_img_0).permute(1, 2, 0))
axs[1].set_title('Averaged Tensor Image')
selected_img_1 = selected_img[None, :]
axs[2].imshow(np.squeeze(selected_img_1).permute(1, 2, 0))
axs[2].set_title('Tensor Image')
plt.show()

Is there a way to cluster an image based on colors and visualize that by using a for loop for example?

I am trying to code the following image classification code:
https://www.thepythoncode.com/article/kmeans-for-image-segmentation-opencv-python
but my question is; is there a way to write a loop such that for each cluster that you use, you get a new image that blackens out this part of the image?
I was trying for example this:
for i in range(0,k):
cluster = i
masked_img[labels == cluster] = [0, 0, 0]
masked_img[i] = masked_img[i].reshape(image.shape)
plt.figure()
plt.imshow(masked_img[i])
plt.show()
with my initially loaded image and k=5 clusters, but what I want is the loop to give me 5 different images with the 5 individual clusters visualized. I don't get how I can manage to fix this, I hope someone else does!
In your approach, I think if you just change labels == cluster to labels != cluster, it should work.
However, here is another way in Python/OpenCV.
Input:
import cv2
import numpy as np
# read input and convert to range 0-1
image = cv2.imread('lake.png')
h, w, c = image.shape
# reshape to 1D array
image_2d = image.reshape(h*w, c).astype(np.float32)
# set number of colors
numcolors = 3
numiters = 10
epsilon = 1
attempts = 10
# do kmeans processing
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, numiters, epsilon)
ret, labels, centers = cv2.kmeans(image_2d, numcolors, None, criteria, attempts, cv2.KMEANS_RANDOM_CENTERS)
# reconstitute 2D image of results
centers = np.uint8(centers)
newimage = centers[labels.flatten()]
newimage = newimage.reshape(image.shape)
cv2.imwrite("lake_kmeans.png", newimage)
cv2.imshow('new image', newimage)
cv2.waitKey(0)
k = 0
for center in centers:
# select center color and create mask
#print(center)
layer = newimage.copy()
mask = cv2.inRange(layer, center, center)
# apply mask to layer
layer[mask == 0] = [0,0,0]
cv2.imshow('layer', layer)
cv2.waitKey(0)
# save kmeans clustered image and layer
cv2.imwrite("lake_layer{0}.png".format(k), layer)
k = k + 1
k-means result:
Layers:

Images are changed to different colors (with pillow), how to get it back to the original colors?

I am trying to find the dominant color in a frame in a video. This works well, however, my frames are somehow converted into different colors. Yellow/pink becomes blue/purple-ish, but black and white stay the same (thus it is not the inverted colors).
Does anyone know where it comes from and how I can change it so that the original colors are kept? This is my code:
import cv2
from sklearn.cluster import KMeans
from collections import Counter
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.patches as patches
video = cv2.VideoCapture('video.mp4')
def show_blurred_image(image, dominant_color):
frame_to_blur = Image.fromarray(image)
blurred_frame = cv2.blur(image, (200,200))
blurred_frame = Image.fromarray(blurred_frame)
plt.subplot(121),plt.imshow(frame_to_blur),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blurred_frame),plt.title('Blurred')
plt.xticks([]), plt.yticks([])
R = round(dominant_color[0])
G = round(dominant_color[1])
B = round(dominant_color[2])
custom_color = '#%02x%02x%02x' % (R, G, B)
print(custom_color)
rect = patches.Rectangle((1620,0),300,1080,linewidth=1,
fill = True,
edgecolor=custom_color,
facecolor=custom_color)
ax = plt.gca()
ax.add_patch(rect)
plt.show()
def get_dominant_color(image, k=4, image_processing_size = None):
"""
takes an image as input
returns the dominant color of the image as a list
dominant color is found by running k means on the
pixels & returning the centroid of the largest cluster
processing time is sped up by working with a smaller image;
this resizing can be done with the image_processing_size param
which takes a tuple of image dims as input
>>> get_dominant_color(my_image, k=4, image_processing_size = (25, 25))
[56.2423442, 34.0834233, 70.1234123]
"""
#resize image if new dims provided
if image_processing_size is not None:
image = cv2.resize(image, image_processing_size,
interpolation = cv2.INTER_AREA)
#reshape the image to be a list of pixels
image = image.reshape((image.shape[0] * image.shape[1], 3))
#cluster and assign labels to the pixels
clt = KMeans(n_clusters = k)
labels = clt.fit_predict(image)
#count labels to find most popular
label_counts = Counter(labels)
#subset out most popular centroid
dominant_color = clt.cluster_centers_[label_counts.most_common(1)[0][0]]
return list(dominant_color)
dominant_colors = []
show_frame = 10
frame_nb = 0
while(video.isOpened()):
ret, frame = video.read()
if ret == True:
if (frame_nb == show_frame):
dominant_color = get_dominant_color(frame)
show_blurred_image(frame, dominant_color)
frame_nb += 1
else:
break
video.release()
cv2.destroyAllWindows()
OpenCV loads images in a BGR format, while PIL and matplotlib works with the RGB format. If you want to use the libraries together, you need to convert the images in the right color spaces.
In your case :
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

How to save specific color image from the output of Kmeans using python

I am using the below code for color-based segmentation using K-means. In this code, each cluster is saving into one image. In my case requirement is a bit different. I want to save only blue color images. Could you please help me how can I save only blue color images?
import numpy as np
import cv2
import pdb
from matplotlib import pyplot as plt
img = cv2.imread('a.png')
Z = np.float32(img.reshape((-1,3)))
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 4
_,labels,centers = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
labels = labels.reshape((img.shape[:-1]))
reduced = np.uint8(centers)[labels]
result = [np.hstack([img, reduced])]
for i, c in enumerate(centers):
mask = cv2.inRange(labels, i, i)
mask = np.dstack([mask]*3) # Make it 3 channel
ex_img = cv2.bitwise_and(img, mask)
ex_reduced = cv2.bitwise_and(reduced, mask)
result.append(np.hstack([ex_img, ex_reduced]))
pdb.set_trace()
cv2.imwrite('watermelon_out.jpg', np.vstack(result))
Original Image
After using this code I am getting result link below:
Expected Result:
This should only print the blue image. First find the center which is closest to blue color and then plot points only in cluster represented by that center
import numpy as np
import cv2
import pdb
from matplotlib import pyplot as plt
img = cv2.imread('a.png')
Z = np.float32(img.reshape((-1,3)))
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 4
_,labels,centers = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
labels = labels.reshape((img.shape[:-1]))
reduced = np.uint8(centers)[labels]
blue_dis = 99999999
blue_center = -1
b = (255, 50 , 0)
for i, c in enumerate(centers):
dis = (c[0]-b[0])**2 + (c[1]-b[1])**2 + (c[1]-b[1])**2
if dis < blue_dis:
blue_center = i
blue_dis = dis
result = [np.hstack([img, reduced])]
for i, c in enumerate(centers):
if i!=blue_center:
continue
mask = cv2.inRange(labels, i, i)
mask = np.dstack([mask]*3) # Make it 3 channel
ex_img = cv2.bitwise_and(img, mask)
ex_reduced = cv2.bitwise_and(reduced, mask)
result.append(np.hstack([ex_img, ex_reduced]))
pdb.set_trace()
cv2.imwrite('watermelon_out.jpg', np.vstack(result))

Categories

Resources