Greyscale Image python Implementation - python

I try to convert a RGB image to grayscale using python as a function but the problem is I give it a RGB image that have height, width and channel but after the code I should have an image with just height and width but it gives me an image with height, width and channel why?
def RGBtoGRAY(img):
height, width, channels = img.shape
grayimg = img
for i in range(height):
for j in range(width):
grayimg[i,j] = 0.3 * image[i,j][0] + 0.59 * image[i,j][1] + 0.11 * image[i,j][2]
return grayimg
the size of the input image is
image.shape
(533, 541, 3)
the size of the output image is
grayimage.shape
(533, 541, 3)
normally I want to find in the size of the output image
(533, 541)

You should avoid using for loops when performing image processing since it is very slow. Instead you can use Numpy which is highly optimized for vector operations. Using this grayscale conversion formula:
gray = R * .299 + G * .587 + B * .114
Method #1: apply_along_axis:
import cv2
import numpy as np
def grayscale(colors):
r, g, b = colors
return 0.299 * r + 0.587 * g + 0.114 * b
# Create image of size 100x100 of random pixels
# Convert to grayscale
image = np.random.randint(255, size=(100,100,3),dtype=np.uint8)
gray = np.apply_along_axis(grayscale, 2, image)
# Display
cv2.imshow('image', image)
cv2.imshow('gray', gray)
cv2.waitKey()
Before -> After
Method #2: cv2.cvtColor
You could use OpenCV directly and read in the image as grayscale with cv2.imread by passing in the cv2.IMREAD_GRAYSCALE or 0 flag to load the image as grayscale.
image = cv2.imread('img.png', cv2.IMREAD_GRAYSCALE) # OR
# image = cv2.imread('img.png', 0)
If you already have the image loaded, you can convert the RGB or BGR image to grayscale using cv2.cvtColor
image = cv2.imread('img.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Assuming you are using a for loop, because you intent to solve it "manually" (like C code), there are number of issues with your implementation:
The assignment grayimg = img in Python does not create a copy of img (the result is that grayimg referencing img).
You meant to use: grayimg = img.copy().
img has 3 dimensions, so when using grayimg = img, grayimg also has 3 dimensions.
You need to create grayimg with two dimensions.
Example for creating grayimg and initialize to zeros:
grayimg = np.zeros((height, width), img.dtype)
Inside the for loop, you are using image instead of img.
Here is a corrected version of RGBtoGRAY:
def RGBtoGRAY(img):
height, width, channels = img.shape
#grayimg = img
# Create height x width array with same type of img, and initialize with zeros.
grayimg = np.zeros((height, width), img.dtype)
for i in range(height):
for j in range(width):
grayimg[i,j] = 0.3 * img[i,j][0] + 0.59 * img[i,j][1] + 0.11 * img[i,j][2]
return grayimg

Related

Increasing Intensity of Certain Image Areas in OpenCV

I currently have the following image and the salience map below which reflects the attention areas of the first image:
Both of them are the same size. What I am trying to do is amplify the region of areas that are very white in the salient region. For example, the eyes, collar and hair would be a bit more highlighted. I have the following code which I have tried to split the image into h, s, v and then multiply through but the output is black and white. Any help is greatly appreciated:
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv_image)
dimensions = (384, 384)
saliencyMap = cv2.resize(saliencyMap, dimensions)
s1 = s * saliencyMap.astype(s.dtype)
hsv_image = cv2.merge([h, s1, v])
out = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)
cv2.imshow('example', out)
cv2.waitKey()
Here is how to do that in Python/OpenCV. Add the two images (from your other post). Modify the mask to have values near a mean of mid-gray. Separate the image into H,S,V channels. Apply the mask to the Saturation channel doing hard light composition. Combine the new saturation with the old hue and value channels and convert back to BGR.
Input 1:
Input 2:
Mask:
import cv2
import numpy as np
# read image 1
img1 = cv2.imread('img1.png')
hh, ww = img1.shape[:2]
# read image 2 and resize to same size as img1
img2 = cv2.imread('img2.png')
img2 = cv2.resize(img2, (ww,hh))
# read saliency mask as grayscale and resize to same size as img1
mask = cv2.imread('mask.png')
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
mask = cv2.resize(mask, (ww,hh))
# add img1 and img2
img12 = cv2.add(img1, img2)
# get mean of mask and shift mean to mid-gray
# desirable for hard light compositing
# add bias as needed
mean = np.mean(mask)
bias = -32
shift = 128 - mean + bias
mask = cv2.add(mask, shift)
# threshold mask at mid gray and convert to 3 channels
# (needed to use as src < 0.5 "if" condition in hard light)
thresh = cv2.threshold(mask, 128, 255, cv2.THRESH_BINARY)[1]
# convert img12 to hsv
hsv = cv2.cvtColor(img12, cv2.COLOR_BGR2HSV)
# separate channels
hue,sat,val = cv2.split(hsv)
# do hard light composite of saturation and mask
# see CSS specs at https://www.w3.org/TR/compositing-1/#blendinghardlight
satf = sat.astype(np.uint8)/255
maskf = mask.astype(np.uint8)/255
threshf = thresh.astype(np.uint8)/255
threshf_inv = 1 - threshf
low = 2.0 * satf * maskf
high = 1 - 2.0 * (1-satf) * (1-maskf)
new_sat = ( 255 * (low * threshf_inv + high * threshf) ).clip(0, 255).astype(np.uint8)
# combine new_sat with old hue and val
result = cv2.merge([hue,new_sat,val])
# save results
cv2.imwrite('img12_sat_hardlight.png', result)
# show results
cv2.imshow('img12', img12)
cv2.imshow('mask', mask)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

Image copy does not show the same image on OpenCV

I would like to display a copy of an image but it does not work.
def display_and_close(img):
cv2.imshow("test",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = cv2.imread('assets/tests.jpeg',0)
width, height = img.shape
new_img = np.zeros((width, height))
new_img[:width, :height] += img[:width, :height]
display_and_close(new_img)
display_and_close(img)
I also tried to iterate over the image like this :
for i in range(img.shape[0]):
for j in range(img.shape[1]):
new_img[i][j] = img[i][j]
but it does not work again
You need to specify the dtype as uint8 for your black image in Python/OpenCV or it will default to float.
So replace
new_img = np.zeros((width, height))
with
new_img = np.zeros((width, height), dtype=np.uint8)
Also note that Numpy and shape use y,x notation (height, width) and you are using (width, height). But since you get the shape reversed also, you are OK. But you should reverse both.

Replacing part of image with a mask in opencv

I have an image that is 384x384 pixels.I want to mask out or replace everything in the middle of the image, so I just have the borders of the image. For this I have created a mask that is 352x352 pixels (just a black image). Now I want to place this mask in the middle of the image so it has a distance of 32 pixels to every corner.
I thought of something like this but it does not work:
mask = cv2.imread('C://Users//but//Desktop//mask.png')
hh, ww, dd = mask.shape
image = cv2.imread('C://Users//but//Desktop//Train//OK//train_image1.jpg')
x = 32
y = 352
print('left:', x, 'top:', y)
# put mask into black background image
mask2 = np.full_like(image, 255)
mask2[x:x + hh, y:y + ww] = mask
# apply mask to image
img_masked = cv2.bitwise_and(image, mask2)
cv2.imshow('masked image', img_masked)
cv2.waitKey(0)
cv2.destroyAllWindows()
The error I get is:
Traceback (most recent call last):
File "C:/Users/but/PycharmProjects/SyntheticDataWriter.py", line 17, in <module>
mask2[x:x + hh, y:y + ww] = mask
ValueError: could not broadcast input array from shape (352,352,3) into shape (352,32,3)
You can make the middle of the image black without creating a black image and overlaying it or anding it, just use Numpy slicing:
import cv2
# Load image and get its dimensions
im = cv2.imread('paddington.png', cv2.IMREAD_COLOR)
h,w = im.shape[0:2]
# Make middle black
im[32:h-32, 32:w-32, :] = 0
cv2.imwrite('result.png', im)
Here's a thumbnail of how he used to be in case anyone doesn't know:
This will help you to add a mask to your image.
import cv2
# image = cv2.imared("path to image") # (384x384X3)
image = np.ones((384, 384, 3), dtype=np.uint8) * 150 # creating 3d image with pixel value 150
h, w, c = image.shape
border_padding = 32 # border width and height you want to set
# we will set pixel value to zero from [32: 384-32, 32: 384-32]
image[border_padding:h-border_padding, border_padding:w-border_padding] = 0 # black mask image
Image

Remove vignette filter of colored image

I am new to Python OpenCV image processing. I want to remove the border/outline shadow of images as shown below. I checked 'how to remove shadow from scanned images' which does not work for me. Is this even possible?
Your problem of border/outline shadows reminded me of the vignette filter. You can have a look at this question if you want to know more about it. So essentially our task to remove the effect of the vignette filter and then increase brightness.
#####VIGNETTE
import cv2
import numpy as np
img = cv2.imread('Paris.jpg')
height, width = img.shape[:2]
original = img.copy()
# generating vignette mask using Gaussian kernels
kernel_x = cv2.getGaussianKernel(width, 150)
kernel_y = cv2.getGaussianKernel(height, 150)
kernel = kernel_y * kernel_x.T
mask = 255 * kernel / np.linalg.norm(kernel)
# applying the mask to each channel in the input image
for i in range(3):
img[:, :, i] = img[:, :, i] * mask
cv2.imshow('Original', original)
cv2.imshow('Vignette', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
To counter the effect change img[:, :, i] = img[:, :, i] * mask to img[:, :, i] = img[:, :, i] / mask
Now we need to increase the brightness of the image. For this, we will convert the image to HSV and increase the values of saturation and value matrices. To know about it in more detail you can refer to this article.
#THE FULL CODE
import cv2
import numpy as np
img = cv2.imread('shadow.jpg')
original = cv2.imread('bright.jpg')
height, width = img.shape[:2]
# generating vignette mask using Gaussian kernels
kernel_x = cv2.getGaussianKernel(width, 150)
kernel_y = cv2.getGaussianKernel(height, 150)
kernel = kernel_y * kernel_x.T
mask = 255 * kernel / np.linalg.norm(kernel)
test = img.copy()
for i in range(3):
test[:, :, i] = test[:, :, i] / mask
hsv = cv2.cvtColor(test, cv2.COLOR_BGR2HSV)
hsv = np.array(hsv, dtype = np.float64)
hsv[:,:,1] = hsv[:,:,1]*1.3 ## scale pixel values up or down for channel 1(Lightness)
hsv[:,:,1][hsv[:,:,1]>255] = 255
hsv[:,:,2] = hsv[:,:,2]*1.3 ## scale pixel values up or down for channel 1(Lightness)
hsv[:,:,2][hsv[:,:,2]>255] = 255
hsv = np.array(hsv, dtype = np.uint8)
test = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
cv2.imshow('Original_bright', original)
cv2.imshow('Original_dark', img)
cv2.imshow('Result', test)
cv2.waitKey(0)
cv2.destroyAllWindows()
The result compared with the original bright image.
How the result would have looked like without the inverse vignette filter.

Error in using adaptive thresholding on gray-scale image

I read an image, and converted it to gray-scale using this function:
def rgb2gray(img):
if len(img.shape)==3 & img.shape[-1] == 3: # img is RGB
return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return img
now, I try to convert my image to binary using this:
def apply_threshold(img):
if len(np.unique(img))==2: #img is already binary
return img
gray_img=rgb2gray(img)
_,binary_img=cv2.threshold(gray_img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
return binary_img
but I get this annoying error:
cv2.error: OpenCV(3.4.1) C:\projects\opencv-python\opencv\modules\imgproc\src\thresh.cpp:1406: error: (-215) src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3)) in function cv::threshold
I can't understand why since gray_img is for sure gray-scale!
I looked at this question, and the top answer by salvador daly proposed that the input picture is not gray-scale, but I checked it multiple times and it for sure is.
Any help will be appreciated!
You can try this approach for getting the threshold version/binary image of color image.
""" Read the original image in color form"""
image = cv2.imread(r'image.png')
""" Convert the image to gray scale"""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
""" reducing the Noise """
blur = cv2.GaussianBlur(gray, (3,3), 0)
""" Applying Otsu thresholding """
_, thres = cv2.threshold(blur, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY_INV)
or if you want adaptive threshold of image, you can try this as well
thres = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY_INV,15,2)
for more details on thresholding you can check this site
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html
also you can check whether the image is of color or gray scale version by checking the shape of the channels of image.
The color image has 3 channels or 3-D matrix ( Red, Green and Blue) so the dimension of the image matrix will be W x H x C (width x height x channel ) for e.g 300 x 300 x 3
The grayscale or binary image has only one channel (gray scale) or it is only 2-D matrix. for e.g 300 x 300.

Categories

Resources