Projecting a grayscale 2D numpy image into RGB? - python

I have a grayscale numpy image (shape=(1024, 1024, 1), dtype=float) that I'm trying to translate into the same image, but with the grayscale values assigned to the red channel (ie. the same image but in redscale).
Here's the original image:
Which is generated using numpy:
def create_mandelbrot_matrix(width, height, max_iter=100):
X = np.linspace(-2, 1, width)
Y = np.linspace(-1, 1, height)
#broadcast X to a square array
C = X[:, None] + 1J * Y
#initial value is always zero
Z = np.zeros_like(C)
exit_times = max_iter * np.ones(C.shape, np.int32)
mask = exit_times > 0
for k in range(max_iter):
Z[mask] = Z[mask] * Z[mask] + C[mask]
mask, old_mask = abs(Z) < 2, mask
#use XOR to detect the area which has changed
exit_times[mask ^ old_mask] = k
return exit_times.T
def mandelbrot_image(width, height, max_iter=100):
mandelbrot_matrix = create_mandelbrot_matrix(width, height, max_iter)
img = np.expand_dims(mandelbrot_matrix, axis=2)
return img
This function results in a totally different image from the original:
def mandelbrot_red_image(w, h):
mandelbrot_img = mandelbrot_image(w, h)
print(mandelbrot_img.shape) # (1024, 1024, 1)
img = np.zeros((w, h, 3))
img[:, :, 0] = mandelbrot_img_int.reshape((w, h))
return img

I dont know how your mandelbrot_image works, but image shapes are usually (h, w), due to the number of lines in a matrix being the first dimension, and the height.
Another point is that, maybe your dtype is not 'uint8', I had to do a conversion in order to the image appear properly.
This code worked for me
from cv2 import cv2
import numpy as np
img = cv2.imread('./mandelbrot.png', cv2.IMREAD_GRAYSCALE)
h, w = img.shape
color_img = np.zeros([h, w, 3])
color_img[:, :, 2] = img # In opencv images are BGR
cv2.imshow('color_mandelbrot', color_img.astype('uint8'))
cv2.waitKey(0)
cv2.destroyAllWindows()

Related

How to label numbers on image with matrix position? (paint by numbers)

First Process I did this
import numpy as np
import cv2
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
import numpy as np
img = cv2.imread("img.jpg")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = np.array(img, dtype=np.float64) / 255
plt.imshow(img)
Then I took the image data to train to find the mean color by k-mean.
w, h, d = original_shape = tuple(img.shape)
print(w, h, d) # 627 783 3
img = np.reshape(img, (w * h, d))
img.shape # (490941, 3)
bit_of_color = 32
kmeans = KMeans(n_clusters=bit_of_color, random_state=0).fit(img)
labels = kmeans.predict(img)
kmeans.labels_ # array([16, 16, 16, ..., 28, 28, 28], dtype=int32)
After that, I created an image to display the color obtained from the model.
image = np.zeros((w, h, d))
mean_ = kmeans.cluster_centers_
d = mean_.shape[1]
mean_[1]
def adjust_image(mean_color_from_model, labels, w, h):
d = mean_color_from_model.shape[1]
print(mean_color_from_model.shape)
image = np.zeros((w, h, d))
label_idx = 0
for i in range(w):
for j in range(h):
image[i][j] = mean_color_from_model[labels[label_idx]]
# print(image[i][j])
label_idx += 1
print(label_idx)
return image
plt.axis('off')
img_kmean = adjust_image(kmeans.cluster_centers_, labels, w, h)
plt.imshow(img_kmean)
And get the following result.
enter image description here
Next, I'll remove the color from the image and still the only the line of the object.
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('img2.png',0)
edges = cv.Canny(img,100,200)
y, x = edges.shape
for i in range(y):
for j in range(x):
if (edges[i][j] == 0):
edges[i][j] = 255
elif (edges[i][j] == 255):
edges[i][j] = 0
plt.imshow(edges,cmap = 'gray')
plt.show()
This is a result.
enter image description here
Then, if I want to label a number into an image using the position of the matrix how to do that?
Desired result. (right image)
enter image description here
Now, My process follows this. But it's still a mistake.
How to make it better?
Follow this Code
def check_bool(x, y, size_x, size_y):
for j in range(y,y + size_y):
for k in range(x,x + size_x):
try:
if (copy_edge[k][j] == 0): #255 W 0 B
return False # ถ้าในกรอบเจอสีดำจะ set เป็น False และนำไป Plot ไม่ได้
break
except:
pass
return True
def put_text(img_plt, text, x, y):
cv2.putText(
img_plt, #numpy array on which text is written
str(text), #text
(x,y), # x y
cv2.FONT_HERSHEY_SIMPLEX, #font family
0.5, #font size
(0, 0, 0, 0), #font color
2) #font stroke
size_x = 20
size_y = 20
copy_edge = edges.copy()
color_edge = img_kmean.copy()
y, x, d = color_edge.shape
for round in range(len(mean_color)):
for y_ in range(0, y, size_y):
for x_ in range(0, x, size_x):
status = check_bool(x_, y_, size_x, size_y) # ถ้า Plot ได้
# print(status)
if status == True: # Putting text
c = sum(color_edge[y_][x_])
c0 = sum(mean_color[round])
if c == c0:
put_text(copy_edge, round, x_, y_)
plt.figure(figsize = (17,10))
plt.imshow(copy_edge,cmap = 'gray')
plt.axis('off')
To label the pixels which are not 0 you can use opencv's cv2connectedComponentsWithStats() function
import numpy as np
import cv2
from skimage.color import label2rgb
# read the image as gray channel
I = cv2.imread("imgPath", 0)
# apply canny
edges = cv2.Canny(img,100,200)
# Invert the canny image
edges = 255 - edges
#dilating the mask to merge some edges(You can skip this step)
#edges = cv2.dilate(edges, np.ones((2,2)))
# label the bw mask from canny (white pixels are labelled automatically)
n, labels, _, _ = cv2.connectedComponentsWithStats(edges)
# convert the labels to RGB for visualization purpose
labels_rgb = np.uint8(255*label2rgb(labels, bg_label=0))
#save it
cv2.imwrite("./Pictures/bw.png", labels_rgb)

Whiten black contours around a skewed image opencv

I have this image:
I want to whiten the black contours (borders) around it without affecting the image content.
Here is the code I used:
import cv2
image = cv2.imread('filename.jpg')
height, width, channels = image.shape
white = [255, 255, 255]
black = [0, 0, 0]
for x in range(0,width):
for y in range(0, height):
channels_xy = image[y, x]
if all(channels_xy == black):
image[y, x] = white
cv2.imwrite('result.jpg', image)
The black borders are whitened (well not 100%), but the writing in the image has been affected too
Is there any suggestion to better whiten to black borders without affecting the image content?
This code can help, but it is very slow. And you need to install the shapely package for python.
import cv2
import numpy as np
from pyutils_gph.utils import showIm
import shapely.geometry as shageo
from tqdm import tqdm, trange
img = cv2.imread('test.jpg')
cv2.imshow('src', img)
# get the gray image and do binaryzation
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray[gray < 100] = 0
gray[gray > 0] = 255
# get the largest boundry of the binary image to locate the target
contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(contours[0])
box = cv2.boxPoints(rect)
box = np.int0(box)
poly = shageo.Polygon(box)
minx = min(box[:, 0])
maxx = max(box[:, 0])
miny = min(box[:, 1])
maxy = max(box[:, 1])
h, w = img.shape[:2]
ind = np.zeros((h, w), np.bool)
# chech the point is inside the target or not
for i in trange(h):
for j in range(w):
if j < minx or j > maxx or i < miny or i > maxy:
ind[i, j] = True
else:
p = shageo.Point(j, i)
if not p.within(poly):
ind[i, j] = True
# make outside point to be white
img[ind] = (255, 255, 255)
cv2.imshow('res', img)
cv2.waitKey(0)
the result is like below.
After some research, I ended to a faster solution (based on the accepted answer). Here is the code:
# import packages
import numpy
import mahotas.polygon
import shapely.geometry as shageo
import cv2
import numpy as np
def get_mask(dims, pts):
# create a numpy array of zeros with the same dimensions of the image
canvas = numpy.zeros((dims[0], dims[1]), dtype=int)
# the points coords in the form of pt(y, x)
# fill the polygon with ones.
mahotas.polygon.fill_polygon(pts, canvas)
return canvas
def find_polygon(img):
# get the gray image and do binaryzation
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray[gray < 20] = 0
gray[gray > 0] = 255
# get the largest boundry of the binary image to locate the target
contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(contours[0])
box = cv2.boxPoints(rect)
box = np.int0(box)
poly = shageo.Polygon(box)
# return the polygone coords in a list
return list(poly.exterior.coords)
def main():
img = cv2.imread('filename.jpg')
# get the coords of the polygon containing (around) the image.
coords = find_polygon(img)
poly_coords = []
# the coords are floats and sometimes are negaive (-1), so transform them into positive ints.
# the mahotas.polygon.fill_polygon function accepts the coords in the form of pt(y, x) so the coords should be reversed
for element in coords:
poly_coords.append(tuple(map(int, map(abs, reversed(element)))))
mask = get_mask(img.shape, poly_coords)
# convert the mask into array of 0 and 1.
binary_mask = np.logical_not(mask).astype(int)
# reshape the array to be similar to the image dimenstions
binary_mask = binary_mask.reshape(img.shape[0], img.shape[1], -1)
# sum the binary mask with the image
cv2.imwrite('res.jpg', img + binary_mask * 255)
main()
Credits:
1- Drawing polygons in numpy arrays
2- Whiten black contours around a skewed image opencv
Here is the result:

Inverting pixels of an RGB image in Python

I'm trying to invert the pixels of an RGB image. That is, simply subtracting the intensity value of each channel (red, green, blue) of each pixel from 255.
I have the following so far:
from PIL import Image
im = Image.open('xyz.png')
rgb_im = im.convert('RGB')
width, height = im.size
output_im = Image.new('RGB', (width,height))
for w in range(width):
for h in range(height):
r,g,b = rgb_im.getpixel((w,h))
output_r = 255 - r
output_g = 255 - g
output_b = 255 - b
output_im[w,h] = (output_r, output_g, output_b)
When I run the above script, I get the following error:
Traceback (most recent call last):
File "image_inverse.py", line 31, in <module>
output_im[w,h] = (output_r, output_g, output_b)
File "/usr/lib/python2.7/dist-packages/PIL/Image.py", line 528, in __getattr__
raise AttributeError(name)
AttributeError: __setitem__
How can I solve this issue?
Thanks.
I guess you can use a vectorized operation if the image is a numpy array
from PIL import Image
im = Image.open('xyz.png')
im = 255 - im
You can use img.putpixel to assign the r,g,b,a values at each pixel-
from PIL import Image
im = Image.open('xyz.png')
rgb_im = im.convert('RGB')
width, height = im.size
output_im = Image.new('RGB', (width,height))
for w in range(width):
for h in range(height):
r,g,b = rgb_im.getpixel((w,h))
output_r = 255 - r
output_g = 255 - g
output_b = 255 - b
alpha = 1
output_im.putpixel((w, h), (output_r, output_g, output_b, alpha))
Convert image to numpy array, and you can perform the operation on all 2-dimensional arrays in one line
from PIL import Image
import numpy as np
image = Image.open('my_image.png')
# Convert Image to numpy array
image_array = np.array(image)
print(image_array.shape)
# Prints something like: (1024, 1024, 4)
# So we have 4 two-dimensional arrays: R, G, B, and the alpha channel
# Do `255 - x` for every element in the first 3 two-dimensional arrays: R, G, B
# Keep the 4th array (alpha channel) untouched
image_array[:, :, :3] = 255 - image_array[:, :, :3]
# Convert numpy array back to Image
inverted_image = Image.fromarray(image_array)
inverted_image.save('inverted.png')

Gradient mask blending in opencv python

I have an image and circle zone. I need to blur all, except for circle zone. Also i need to make border of circle smooth.
The input:
The output(made it in image redactor with mask, but i think opencv is using only bitmap masks):
For now i have code in python, which isn't blurring border of circle.
def blur_image(cv_image, radius, center, gaussian_core, sigma_x):
blurred = cv.GaussianBlur(cv_image, gaussian_core, sigma_x)
h, w, d = cv_image.shape
# masks
circle_mask = np.ones((h, w), cv_image.dtype)
cv.circle(circle_mask, center, radius, (0, 0, 0), -1)
circle_not_mask = np.zeros((h, w), cv_image.dtype)
cv.circle(circle_not_mask, center, radius, (2, 2, 2), -1)
# Computing
blur_around = cv.bitwise_and(blurred, blurred, mask=circle_mask)
image_in_circle = cv.bitwise_and(cv_image, cv_image, mask=circle_not_mask)
res = cv.bitwise_or(blur_around, image_in_circle)
return res
Current version:
How can i blur the border of circle? In example of output i've used gradient mask in program. Is there something similar in opencv?
UPDATE 04.03
So, i've tried formula from this answered topic and what i have:
Code:
def blend_with_mask_matrix(src1, src2, mask):
res = src2 * (1 - cv.divide(mask, 255.0)) + src1 * cv.divide(mask, 255.0)
return res
This code should work similar as recent one, but it doesn't. The image in circle is slightly different. It has some problems with color. The question is still open.
I think maybe you want something like that.
This is the source image:
The source-blured-pair :
The mask-alphablened-pair:
The code with description in the code comment.
#!/usr/bin/python3
# 2018.01.16 13:07:05 CST
# 2018.01.16 13:54:39 CST
import cv2
import numpy as np
def alphaBlend(img1, img2, mask):
""" alphaBlend img1 and img 2 (of CV_8UC3) with mask (CV_8UC1 or CV_8UC3)
"""
if mask.ndim==3 and mask.shape[-1] == 3:
alpha = mask/255.0
else:
alpha = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)/255.0
blended = cv2.convertScaleAbs(img1*(1-alpha) + img2*alpha)
return blended
img = cv2.imread("test.png")
H,W = img.shape[:2]
mask = np.zeros((H,W), np.uint8)
cv2.circle(mask, (325, 350), 40, (255,255,255), -1, cv2.LINE_AA)
mask = cv2.GaussianBlur(mask, (21,21),11 )
blured = cv2.GaussianBlur(img, (21,21), 11)
blended1 = alphaBlend(img, blured, mask)
blended2 = alphaBlend(img, blured, 255- mask)
cv2.imshow("blened1", blended1);
cv2.imshow("blened2", blended2);
cv2.waitKey();cv2.destroyAllWindows()
Some useful links:
Alpha Blending in OpenCV C++ : Combining 2 images with transparent mask in opencv
Alpha Blending in OpenCV Python:
Gradient mask blending in opencv python
So the main problem with (mask/255) * blur + (1-mask/255)*another img was operators. They were working only with one channel. Next problem is working with float numbers for "smoothing".
I've changed code of blending with alpha channel to this:
1) i'm taking every channel for source images and mask
2) Performing formula
3) Merging channels
def blend_with_mask_matrix(src1, src2, mask):
res_channels = []
for c in range(0, src1.shape[2]):
a = src1[:, :, c]
b = src2[:, :, c]
m = mask[:, :, c]
res = cv.add(
cv.multiply(b, cv.divide(np.full_like(m, 255) - m, 255.0, dtype=cv.CV_32F), dtype=cv.CV_32F),
cv.multiply(a, cv.divide(m, 255.0, dtype=cv.CV_32F), dtype=cv.CV_32F),
dtype=cv.CV_8U)
res_channels += [res]
res = cv.merge(res_channels)
return res
And as a gradient mask i'm just using blurred circle.
def blur_image(cv_image, radius, center, gaussian_core, sigma_x):
blurred = cv.GaussianBlur(cv_image, gaussian_core, sigma_x)
circle_not_mask = np.zeros_like(cv_image)
cv.circle(circle_not_mask, center, radius, (255, 255, 255), -1)
#Smoothing borders
cv.GaussianBlur(circle_not_mask, (101, 101), 111, dst=circle_not_mask)
# Computing
res = blend_with_mask_matrix(cv_image, blurred, circle_not_mask)
return res
Result:
It is working a bit slower than very first version without smoother borders, but it's ok.
Closing question.
You can easily mask upon an image using the following funciton:
def transparentOverlay(src, overlay, pos=(0, 0), scale=1):
overlay = cv2.resize(overlay, (0, 0), fx=scale, fy=scale)
h, w, _ = overlay.shape # Size of foreground
rows, cols, _ = src.shape # Size of background Image
y, x = pos[0], pos[1] # Position of foreground/overlay image
# loop over all pixels and apply the blending equation
for i in range(h):
for j in range(w):
if x + i >= rows or y + j >= cols:
continue
alpha = float(overlay[i][j][3] / 255.0) # read the alpha channel
src[x + i][y + j] = alpha * overlay[i][j][:3] + (1 - alpha) * src[x + i][y + j]
return src
You need to pass the source image, then the overlay mask and position where you want to set the mask.
You can even set the masking scale. by calling it like this way.
transparentOverlay(face_cigar_roi_color,cigar,(int(w/2),int(sh_cigar/2)))
For details you can look at this link: Face masking and Overlay using OpenCV python
Output:
You can try using a function from PIL library.
example -
from PIL import Image, ImageFilter
blur_factor = 3 # for smooth borders as you have mentioned
blurred_mask = mask.filter(ImageFilter.GaussianBlur(blur_factor)) # your circle = 255, background = 0
final_img = Image.composite(blurred_img, original_img, blurred_mask) # here blurred image is the one which you have already blurred, original image is your sharp non blurred image

How to add motion blur to numpy array

I have a numpy array from image
So, is there a good way to do so:
from PIL import Image
a = Image.open('img')
a = a.filter(MOTION_BLUR)
import cv2
import numpy as np
img = cv2.imread('input.jpg')
cv2.imshow('Original', img)
size = 15
# generating the kernel
kernel_motion_blur = np.zeros((size, size))
kernel_motion_blur[int((size-1)/2), :] = np.ones(size)
kernel_motion_blur = kernel_motion_blur / size
# applying the kernel to the input image
output = cv2.filter2D(img, -1, kernel_motion_blur)
cv2.imshow('Motion Blur', output)
cv2.waitKey(0)
explanation you can found here
draw a rotated line as kernel, then apply a convolution filter to an image with that kernel.
The code below uses opencv framework.
import cv2
import numpy as np
#size - in pixels, size of motion blur
#angel - in degrees, direction of motion blur
def apply_motion_blur(image, size, angle):
k = np.zeros((size, size), dtype=np.float32)
k[ (size-1)// 2 , :] = np.ones(size, dtype=np.float32)
k = cv2.warpAffine(k, cv2.getRotationMatrix2D( (size / 2 -0.5 , size / 2 -0.5 ) , angle, 1.0), (size, size) )
k = k * ( 1.0 / np.sum(k) )
return cv2.filter2D(image, -1, k)
if you want to apply vertical, you can use this kernel:
kernel_motion_blur = np.zeros((size, size))
kernel_motion_blur[int(:, (size-1)/2)] = np.ones(size)
kernel_motion_blur = kernel_motion_blur / size
I would use matplotlib:
from PIL import Image
img = Image.open('your_image')
imgplot = plt.imshow(img, interpolation="bicubic")

Categories

Resources