Scaling an image makes no discernable change - python

I am using two different ways to re-size an image, but all three look exactly the same...
What am I doing wrong that no scaling occurs?
import cv2 as cv
import numpy as np
path = "resources/Shapes.png"
img = cv.imread(path)
cv.imshow("img", img)
res1 = cv.resize(img, None, fx = 2, fy = 2, interpolation = cv.INTER_CUBIC)
cv.imshow("res1", res1)
height, width = img.shape[:2]
res2 = cv.resize(img, (2 * width, 2 * height), interpolation = cv.INTER_CUBIC)
cv.imshow("res2", res2)
k = cv.waitKey(0)

Just putting this here for future reference:
The code above works, the issue was that imshow does not always show the true size of the image, by saving the different images, or simply examining them with res1.shape vs img.shape, you can see the true size of the image.

Related

padding image array with gray background

I am comparing thumbnail images by showing them side by side using Image.fromarray(np.haystack(<list of image array>).show(). The problem is that the image arrays have different sizes. My solution is to pad the array with a background gray color (200, 200, 200) and make all arrays equal size 200x200.
My question does numpy have a more direct way of doing this?
My solution:
def pad_with_gray_backgound(_array, size):
array_padded = np.ones((size, size, 3), dtype=np.uint8)*200
for i in range(array_padded.shape[0]):
for j in range(array_padded.shape[1]):
try:
array_padded[i, j] = _array[i, j]
except IndexError:
pass
return array_padded
and to call this function
import numpy as np
from PIL import Image
image_arrays = []
for pic in pic_selection:
pic_thumbnail = io.BytesIO(pic.thumbnail.encode('ISO-8859-1'))
padded_image_array = pad_with_gray_background(
np.array(Image.open(pic_thumbnail)), 200)
image_arrays.append(padded_image_array)
Image.fromarray(np.hstack(image_arrays)).show()
note pic.thumbnail is a bytes object taken from the exif
Answer by Mark Setchell is to use slicing:
array_padded[0:height, 0:width, :] = image_array[:]
Just have make sure that the shape of image_array is not bigger than array_padded.
import numpy as np
from PIL import Image
image_arrays = []
for pic in pic_selection:
pic_thumbnail = io.BytesIO(pic.thumbnail.encode('ISO-8859-1'))
image_array = np.array(Image.open(pic_thumbnail))
height, width = (200, 200)
array_padded = np.ones((height, width, 3), dtype=np.uint8)*200
height = min(image_array.shape[0], height)
width = min(image_array.shape[1], width)
array_padded[0:height, 0:width, :] = image_array[0:height, 0:width, :]
image_arrays.append(array_padded)
Image.fromarray(np.hstack(image_arrays)).show()

How to map pixels to values in a new image with python?

I'm trying to map an image to a velocity model, where the colors represent the velocities, so I read an image using OpenCV, take its dimensions, create an array of velocities within a certain range and try to recreate the image with these values, I saw an algoirthm very similar in matlab that works:
` vi=1000;
vf=4200;
M=imread('modelo_vr_2500x300.png');
Lx=size(M,1);
Ly=size(M,2);
N=M(1:Lx,1:Ly,1);
cor2=0:255;
vel2=cor2/256*(vf-vi)+vi;
V=zeros(size(N));
for i=1:length(cor2)
V=V+vel2(i)*(N==cor2(i));
end
imagesc(V)
colorbar`
So I tried to adapt it to Python, but it doesn't seem to work, all that I get is an image totally black, but if I print V, the new image, it has values, but they are quite high. I have no idea what I'm doing wrong, could anyone help?
import CV2
# read image
img = cv2.imread("figures/teste03-06B.png", cv2.IMREAD_UNCHANGED)
# get dimensions of image
dimensions = img.shape
# height = Ly, width = Lx, number of channels in image = Ch
Ly = img.shape[0]
Lx = img.shape[1]
Ch = img.shape[2]
N=img[0:Ly, 0:Lx, 0:Ch]
print('Image Dimension : ',dimensions)
print('Image Height : ',Ly)
print('Image Width : ',Lx)
print('Number of Channels : ',Ch)
cv2.imshow("Display window", img)
cv2.waitKey(0)
cv2.destroyWindow("Display window")
import numpy as np
vi=2000
vf=6000
color=np.array(range(256))
vel=((color/256)*(vf-vi))+vi
V = np.zeros_like(img)
for i in range(0,len(color)):
if N[i]==color[i]:
V=V+vel[i]
else:
V=V
print(V)
cv2.imshow("Display window", V)
cv2.waitKey(0)
cv2.destroyWindow("Display window")`
It doesn't give any error message, just doesn't work as it should, I have no idea why...
I think you're looking for this:
new_image = vel[img]
But you probably want to normalize vel first. Some variant of:
vel = vel/max(vel) * 255
vel = vel.astype(dtype=np.uint8)
I think that should work.

Is there a better way to separate the writing from the background?

I am working on a project where I should apply and OCR on some documents.
The first step is to threshold the image and let only the writing (whiten the background).
Example of an input image: (For the GDPR and privacy reasons, this image is from the Internet)
Here is my code:
import cv2
import numpy as np
image = cv2.imread('b.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
h = image.shape[0]
w = image.shape[1]
for y in range(0, h):
for x in range(0, w):
if image[y, x] >= 120:
image[y, x] = 255
else:
image[y, x] = 0
cv2.imwrite('output.jpg', image)
Here is the result that I got:
When I applied pytesseract to the output image, the results were not satisfying (I know that an OCR is not perfect). Although I tried to adjust the threshold value (in this code it is equal to 120), the result was not as clear as I wanted.
Is there a way to make a better threshold in order to only keep the writing in black and whiten the rest?
After digging deep in StackOverflow questions, I found this answer which is about removing watermark using opencv.
I adapted the code to my needs and this is what I got:
import numpy as np
import cv2
image = cv2.imread('a.png')
img = image.copy()
alpha =2.75
beta = -160.0
denoised = alpha * img + beta
denoised = np.clip(denoised, 0, 255).astype(np.uint8)
#denoised = cv2.fastNlMeansDenoising(denoised, None, 31, 7, 21)
img = cv2.cvtColor(denoised, cv2.COLOR_BGR2GRAY)
h = img.shape[0]
w = img.shape[1]
for y in range(0, h):
for x in range(0, w):
if img[y, x] >= 220:
img[y, x] = 255
else:
img[y, x] = 0
cv2.imwrite('outpu.jpg', img)
Here is the output image:
The good thing about this code is that it gives good results not only with this image, but also with all the images that I tested.
I hope it helps anyone who had the same problem.
You can use adaptive thresholding. From documentation :
In this, the algorithm calculate the threshold for a small regions of the image. So we get different thresholds for different regions of the same image and it gives us better results for images with varying illumination.
import numpy as np
import cv2
image = cv2.imread('b.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image = cv2.medianBlur(image ,5)
th1 = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,11,2)
th2 = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
cv2.imwrite('output1.jpg', th1 )
cv2.imwrite('output2.jpg', th2 )

get ValueError: bad transparency mask when trying to paste, image and mask are the same

import numpy as np
from PIL import Image
img_orig = Image.open("me.jpg")
# convert pic to 3-D array
array_orig = np.array(img_orig)
# create R image
array_r = np.copy(array_orig)
array_r[:, :, 1:3] = 0
img_r = Image.fromarray(array_r)
# create GB image
array_gb = np.copy(array_orig)
array_gb[:, :, 0] = 0
img_gb = Image.fromarray(array_gb)
canvas_r = Image.new("RGB", img_orig.size, color=(0,0,0))
canvas_r.paste(img_r, (5, 5), img_r) #error line
canvas_gb = Image.new("RGB", img_orig.size, color=(0,0,0))
canvas_gb.paste(img_gb, (0, 0), img_gb)
result_array = np.array(canvas_r) + np.array(canvas_gb)
result = Image.fromarray(result_array)
result.show()
The line of code gives error is given in the code. I don't quite understand why PIL gives transparency error for same image. I believe there is no need to specify mode because the mask and image are the same.
I solved a similiar problem with:
Replace img_orig = Image.open("me.jpg")
with img_orig = Image.open("me.jpg").convert('RGBA')
And replace canvas_gb = Image.new("RGB", img_orig.size, color=(0,0,0))
with canvas_gb = Image.new("RGBA", img_orig.size, color=(0,0,0))
The image is lacking alpha value
img_orig = Image.open("me.jpg")
img_orig.putalpha(255)
solved the issue.

OpenCV subplots images with titles and space around borders

I am looking to display some images in OpenCV Python with titles and borders around the each subplot. something like this (courtesy of the following stackoverflow post: OpenCV (Python) video subplots):
WHAT I WANT:
But I only manage to get this with that code adapted.
import cv2
im1 = cv2.imread('Lenna.png')
final_frame = cv2.hconcat((im1, im1))
cv2.imshow('lena', final_frame)
WHAT I HAVE
Is it possible to obtain this using OpenCV?
I know a workaround would be to put text on the images, but that's not what I want because it will cover important information that way.
UPDATE
My bad, I didn't specify initially: I have 4 subplots (so 4 different images) and not two like in the example. Also, I want the solution to be as fast as possible since I have video (time restrictions)
I have a pretty quick and dirty solution. You can refine it to suit your needs. I have the explanation alongside the code as well:
import cv2
import numpy as np
img1 = cv2.imread('lena.jpg')
#--- Here I am creating the border---
black = [0,0,0] #---Color of the border---
constant=cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT,value=black )
cv2.imshow('constant',constant)
You can find many other options for different borders ON THIS PAGE
#--- Here I created a violet background to include the text ---
violet= np.zeros((100, constant.shape[1], 3), np.uint8)
violet[:] = (255, 0, 180)
#--- I then concatenated it vertically to the image with the border ---
vcat = cv2.vconcat((violet, constant))
cv2.imshow('vcat', vcat)
#--- Now I included some text ---
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(vcat,'FRAME',(30,50), font, 2,(0,0,0), 3, 0)
cv2.imshow('Text', vcat)
#--- I finally concatenated both the above images horizontally---
final_img = cv2.hconcat((vcat, vcat))
cv2.imshow('Final', final_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
The general idea would be to create a new image with width += width/10 and height += height/20. Write some text as heading and place the input image along the center as:
import cv2
import numpy as np
img = cv2.imread("/Users/anmoluppal/Downloads/Lenna.png")
height, width, ch = img.shape
new_width, new_height = width + width/20, height + height/8
# Crate a new canvas with new width and height.
canvas = np.ones((new_height, new_width, ch), dtype=np.uint8) * 125
# New replace the center of canvas with original image
padding_top, padding_left = 60, 10
if padding_top + height < new_height and padding_left + width < new_width:
canvas[padding_top:padding_top + height, padding_left:padding_left + width] = img
else:
print "The Given padding exceeds the limits."
text1 = "Sample Image 1"
text2 = "Sample Image 2"
img1 = cv2.putText(canvas.copy(), text1, (int(0.25*width), 30), cv2.FONT_HERSHEY_COMPLEX, 1, np.array([255, 0, 0]))
img2 = cv2.putText(canvas.copy(), text2, (int(0.25*width), 30), cv2.FONT_HERSHEY_COMPLEX, 1, np.array([255, 0, 0]))
final = cv2.hconcat((img1, img2))
cv2.imwrite("./debug.png", final)
I used the other answers to make a generalizable function which works for arbitrary row/columns:
def cvSubplot(imgs, # 2d np array of imgs (each img an np arrays of depth 1 or 3).
pad=10, # number of pixels to use for padding between images. must be even
titles=None, # (optional) np array of subplot titles
win_name='CV Subplot' # name of cv2 window
):
'''
Makes cv2 based subplots. Useful to plot image in actual pixel size
'''
rows, cols = imgs.shape
subplot_shapes = np.array([list(map(np.shape, x)) for x in imgs])
sp_height, sp_width, depth = np.max(np.max(subplot_shapes, axis=0), axis=0)
title_pad = 30
if titles is not None:
pad_top = pad + title_pad
else:
pad_top = pad
frame = np.zeros((rows*(sp_height+pad_top), cols*(sp_width+pad), depth ))
for r in range(rows):
for c in range(cols):
img = imgs[r, c]
h, w, _ = img.shape
y0 = r * (sp_height+pad_top) + pad_top//2
x0 = c * (sp_width+pad) + pad//2
frame[y0:y0+h, x0:x0+w, :] = img
if titles is not None:
frame = cv2.putText(frame, titles[r, c], (x0, y0-title_pad//4), cv2.FONT_HERSHEY_COMPLEX, .5, (255,255,255))
cv2.imshow(win_name, frame)
cv2.waitKey(0)
Below is an example usage:
import cv2
import numpy as np
a1 = np.random.random((40,400,1))
a2 = np.random.random((200,200,1))
a3 = np.random.random((100,100,1))
a4 = np.random.random((300,150,1))
a5 = np.random.random((100,150,1))
filler = np.zeros((0,0,1))
titles = np.array([['A', 'B', 'C'], ['D', 'E', 'Filler']])
imgs = np.array([[a1, a2, a3], [a4, a5, filler]])
cvSubplot(imgs, pad=20, titles=titles)
That script produces the following cv2 image:

Categories