RGB to grayscale without cmaps - python

is there a way to change an image to grayscale without using cmap in matpotlib?
my function is
def grayscale(image):
img = image.copy()
r=img[:,:,0]*0.3
g=img[:,:,1]*0.59
b=img[:,:,2]*0.11
gray=r+g+b
img=np.dstack((gray,gray,gray))
return img
plt.imshow(img)
However, the image I got is just black and white, not in grayscale TT. Then when I tried using gray=r+g+b,plt.imshow(img), I got a green and yellow picture. I have tried searching everywhere to get clues and all I found was the use of cmaps. However the project I am doing doesn't allow us to use cmap.

Here is a working solution using your code - you should add your picture path in the image_path variable:
import numpy as np
from PIL import Image
import cv2
def grayscale(image):
img = np.asarray(Image.open(image))
r = img[:,:,0]*0.3
g = img[:,:,1]*0.59
b = img[:,:,2]*0.11
gray = r+g+b
return gray
image_path = "test.jpg"
img = grayscale(image_path)
cv2.imwrite('greyscale.jpg',img)
The trick was that you did not load the image in a proper way.

Related

CV2 add 2 images without transparency Python

I'm working on an image processing project. I have 2 RGB images and I would like to create a new one by overlapping the 2 input images.
This is my 2 input images:
I'm using the cv2 "add" method to achieve this. But the result is not the hoped result...
The result is as follows:
I don't want to have a transparency on my red stripe. I would like something opaque, which covers the crack on the image 1.
There is my code:
img1 = cv2.imread(r"C:\Users\francois.bock\Desktop\crack.jpg")
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
img2 = cv2.imread(r"C:\Users\francois.bock\Desktop\line.png")
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
# Adding two images
add = cv2.add(img1, img2)
#Display the image
img = PImage.fromarray(add)
img.show()
I also naively tried to add the 2 images like that:
add = img1 + img2
add = add.astype(np.uint8)
But the result is even stranger.
I also used the cv2 "addWeighted" method, but it also gives a transparent stripe.
So my question is, is there a simple way to add 2 images without transparency? Thank you in advance.
You can do it with straight Numpy:
import numpy as np
import cv2
a = cv2.imread('a.jpg')
b = cv2.imread('b.png')
# Make "a" red anywhere b>0
a[ np.any(b>0,axis=-1) ] = [0,0,255]
If you happen to have blue and green or any other colour lines lurking in your image too, you can use this:
# Anywhere image "b" is not black, use image "b", else use image "a"
result = np.where(np.any(b>0,axis=-1,keepdims=True), b, a)

Load RGB images as a ndarray, and plot out with color change

I tried to load and plot several images(jpg) from a local folder, and found out the plotting images changed color. The color channel correction between OpenCV and Matplotlib has been done.
How did it happen? How to correct the color?
Thanks.
import cv2
from matplotlib import pyplot as plt
import numpy as np
import os
folder = 'New_Web_Image'
img_list = np.empty([0,32,32,3])
for file in os.listdir(folder):
img = cv2.imread(os.path.join(folder, file))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (32,32), interpolation = cv2.INTER_AREA)
#plt.imshow(img)
#plt.show()#If I plot the image here, the image show right color
img_list = np.append(img_list, [img[:, :, :]], axis=0)
print(img_list.shape) #lists shape check right
plt.imshow(img_list[0])
plt.show() #If I plor the image from the lists, the color changed
Here is the image result in the loop:
Here is the image from ndarray "lists":
It's not a color correction. OpenCV orders layers as BGR, rather than the RGB we usually expect. As long as you're staying with the OpenCV world, that should be fine. But anding and image loaded via cv2.imread() to matplotlib.pyplot steps outside that world, which is why you need
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
to get the layers reordered first.
A bunch of other interesting (and possibly useful) conversions are possible. See http://docs.opencv.org/3.2.0/df/d9d/tutorial_py_colorspaces.html
To halfly answer my own question, I've corrected the colors by
loading the images with a ndarray output first,
and then changing color & size, and plotting the images
Updated code:
import cv2
from matplotlib import pyplot as plt
import numpy as np
import os
# Load the images
folder = 'New_Web_Image'
img_list = []
for file in os.listdir(folder):
img = cv2.imread(os.path.join(folder, file))
if img is not None:
img_list.append(img)
img_list = np.asarray(img_list)
# Plot the images
n = img_list.shape[0]
fig, axs = plt.subplots(1, n, figsize=(20,5), dpi=80)
for i in range(n):
img = img_list[i]
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (32,32), interpolation = cv2.INTER_AREA)
axs[i].imshow(img)
plt.show()
Another half question, that "how did the color change in previous code?" is still unclear to me.
Thanks in advance to who would suggest.

How to crop the background from an inclined image?

The input is an image(document) from the scanner and my task is to crop the background and return only the document, just like this: Input Output
I've done this through thresholding and getbbox:
import matplotlib.pyplot as plt
import matplotlib.image as pli
from skimage.filters import threshold_otsu as otsu
from PIL import Image
cnh_gray = Image.open("cnh.jpg").convert('L')
cnh_gray.save('cnhgray.jpg')
img = pli.imread('cnhgray.jpg')
imagem = Image.open('cnhgray.jpg')
thresh = otsu(img)
mask = img < thresh
msk = Image.fromarray(mask,'L')
box = msk.getbbox()
crop = imagem.crop(box)
The problem is: The getbbox function doesn't work when the document isn't vertical. Since I don't know the angle, how can I rotate the image to use the getbbox funcion? If there's another function that I can use for inclined images instead of getbbox, please tell me.
Thanks for the help.

Trouble with Canny Edge Detector - Returning black image

I'm trying to run the canny edge detector on this image:
With this code:
def edges(img):
from skimage import feature
img = Image.open(img)
img.convert('L')
array = np.array(img)
out = feature.canny(array, sigma=1, )
return Image.fromarray(out,'L')
edges('Q_3.jpg').save('Q_3_edges.jpg')
But I'm just getting a black image back. Any ideas what I could be doing wrong? I tried sigma of 1 and of 3.
I have the same situation and this helps for me. Before use the Canny filter, just convert your elements of image array to float32 type:
array = np.array(img)
array = array.astype('float32')
out = feature.canny(array, sigma=1, )
Your images need to be in the correct range for the relevant dtype, as discussed in the user manual here: http://scikit-image.org/docs/stable/user_guide/data_types.html
This should be automatically handled if you use the scikit-image image I/O functions:
from skimage import io
img = io.imread('Q_3.jpg')
So the issue was with the canny function returning and array of type boolean.
Oddly, setting the Image.fromarray mode to '1' didn't help. Instead this was the only way I could get it working; converting the output array to grayscale:
def edges(img):
from skimage import feature
img = Image.open(img)
img.convert('L')
array = np.array(img)
out = np.uint8(feature.canny(array, sigma=1, ) * 255)
return Image.fromarray(out,mode='L')
The problem happens when the image is loaded as float (i.e. in the range 0-1). The loader does that for some types of images. You can check the type of the loaded image by:
print(img.dtype)
If the output is something like float64 (i.e. not uint8), then your image is in the range 0-1.
Canny expects an image in the range 0-255. Therefore, the solution is as easy as:
from skimage import img_as_ubyte
img = io.imread("an_image.jpg")
img = img_as_ubyte(img)
Hope this helps,
The problem happens when the image is saved. You can save image with other library like matplotlib:
import numpy as np
import matplotlib.pyplot as plt
from skimage import feature
from skimage import io
def edges(img):
img = io.imread(img)
array = np.array(img)
out = feature.canny(array, sigma=1, )
return out
plt.imsave("canny.jpg", edges("input.jpg"), cmap="Greys")

Using PIL to turn a RGB image into a pure black and white image

I'm using the Python Imaging Library for some very simple image manipulation, however I'm having trouble converting a greyscale image to a monochrome (black and white) image. If I save after changing the image to greyscale (convert('L')) then the image renders as you would expect. However, if I convert the image to a monochrome, single-band image it just gives me noise as you can see in the images below. Is there a simple way to take a colour png image to a pure black and white image using PIL / python?
from PIL import Image
import ImageEnhance
import ImageFilter
from scipy.misc import imsave
image_file = Image.open("convert_image.png") # open colour image
image_file= image_file.convert('L') # convert image to monochrome - this works
image_file= image_file.convert('1') # convert image to black and white
imsave('result_col.png', image_file)
from PIL import Image
image_file = Image.open("convert_image.png") # open colour image
image_file = image_file.convert('1') # convert image to black and white
image_file.save('result.png')
yields
A PIL only solution for creating a bi-level (black and white) image with a custom threshold:
from PIL import Image
img = Image.open('mB96s.png')
thresh = 200
fn = lambda x : 255 if x > thresh else 0
r = img.convert('L').point(fn, mode='1')
r.save('foo.png')
With just
r = img.convert('1')
r.save('foo.png')
you get a dithered image.
From left to right the input image, the black and white conversion result and the dithered result:
You can click on the images to view the unscaled versions.
Another option (which is useful e.g. for scientific purposes when you need to work with segmentation masks) is simply apply a threshold:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Binarize (make it black and white) an image with Python."""
from PIL import Image
from scipy.misc import imsave
import numpy
def binarize_image(img_path, target_path, threshold):
"""Binarize an image."""
image_file = Image.open(img_path)
image = image_file.convert('L') # convert image to monochrome
image = numpy.array(image)
image = binarize_array(image, threshold)
imsave(target_path, image)
def binarize_array(numpy_array, threshold=200):
"""Binarize a numpy array."""
for i in range(len(numpy_array)):
for j in range(len(numpy_array[0])):
if numpy_array[i][j] > threshold:
numpy_array[i][j] = 255
else:
numpy_array[i][j] = 0
return numpy_array
def get_parser():
"""Get parser object for script xy.py."""
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
parser = ArgumentParser(description=__doc__,
formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("-i", "--input",
dest="input",
help="read this file",
metavar="FILE",
required=True)
parser.add_argument("-o", "--output",
dest="output",
help="write binarized file hre",
metavar="FILE",
required=True)
parser.add_argument("--threshold",
dest="threshold",
default=200,
type=int,
help="Threshold when to show white")
return parser
if __name__ == "__main__":
args = get_parser().parse_args()
binarize_image(args.input, args.output, args.threshold)
It looks like this for ./binarize.py -i convert_image.png -o result_bin.png --threshold 200:
As Martin Thoma has said, you need to normally apply thresholding. But you can do this using simple vectorization which will run much faster than the for loop that is used in that answer.
The code below converts the pixels of an image into 0 (black) and 1 (white).
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
#Pixels higher than this will be 1. Otherwise 0.
THRESHOLD_VALUE = 200
#Load image and convert to greyscale
img = Image.open("photo.png")
img = img.convert("L")
imgData = np.asarray(img)
thresholdedData = (imgData > THRESHOLD_VALUE) * 1.0
plt.imshow(thresholdedData)
plt.show()
A simple way to do it using python :
Python
import numpy as np
import imageio
image = imageio.imread(r'[image-path]', as_gray=True)
# getting the threshold value
thresholdValue = np.mean(image)
# getting the dimensions of the image
xDim, yDim = image.shape
# turn the image into a black and white image
for i in range(xDim):
for j in range(yDim):
if (image[i][j] > thresholdValue):
image[i][j] = 255
else:
image[i][j] = 0
this is how i did it its havd better results like a gray filter
from PIL import Image
img = Image.open("profile.png")
BaW = img.convert("L")
BaW.save("profileBaW.png")
BaW.show()

Categories

Resources