Wrong colours with cv2.imdecode (python opencv) - python-3.x

I try to display the image located here: http://skyservice.pha.jhu.edu/DR12/ImgCutout/getjpeg.aspx?ra=118.70299999999999&dec=45.721000000000004&width=10&height=10&scale=0.6
The image looks like this:
I use this code:
import matplotlib.pyplot as plt
import numpy as np
import urllib
import cv2
url = 'http://skyservice.pha.jhu.edu/DR12/ImgCutout/getjpeg.aspx?ra=118.70299999999999&dec=45.721000000000004&width=10&height=10&scale=0.6'
def url_to_image(url):
resp = urllib.request.urlopen(url)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
image = cv2.imdecode(image, cv2.IMREAD_COLOR)
return image
img = url_to_image(url)
plt.imshow(img)
And it displays this:
Where all colours are too blue. I have tried various possibilities to change cv2.IMREAD_COLOR with values found in the manual, on StackOverflow or elsewhere on the net, like -1, 0, 1, cv2.COLOR_BGR2RGB, ... but I haven't been able to get the right colours. I have tried cv2.COLOR_BGR2GRAY, it didn't even show in gray scales. I even tried this answer, but cv2.CV_LOAD_IMAGE_COLORdoesn't seem to exist anymore...
Is there a correct value of cv2.imdecode() flag, or a special colormap of plt.imshow(), which would give me the initial colours?

Thanks to Mark Setchell, it now works. I quote him:
matplotlib requires RGB ordering whereas OpenCV (perversely) uses BGR
Therefore, the correct code is
def url_to_image(url):
resp = urllib.request.urlopen(url)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
imageBGR = cv2.imdecode(image, cv2.IMREAD_COLOR)
imageRGB = cv2.cvtColor(imageBGR , cv2.COLOR_BGR2RGB)
return image

Related

Color diffusion when merging multiple images in a folder using PIL in python

I have set of 17 images and one of them has a highlighted pixel for my use. But, when I merge these 17 images, I get the color but it diffuses out of the pixel boundaries and I start seeing some colored pixel in black background.
I am using PIL library for the merging. I am attaching my code and the images for the reference. Any help would be appreciated.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# Cretaing the Pixel array
from PIL import Image
from PIL import ImageColor
img_path = '/Volumes/MY_PASSPORT/JRF/cancer_genome/gopal_gen/png_files/'
image_list = []
for entry in os.listdir(img_path):
if entry.endswith('.png'):
entry = int(entry.rstrip('.csv.png'))
image_list.append(entry)
image_list.sort()
list_img = []
for j in range(len(image_list)):
stuff = str(image_list[j])+'.csv.png'
list_img.append(stuff)
#print(list_img[0])
images = [Image.open(img_path+x) for x in list_img]
widths, heights = zip(*(i.size for i in images))
total_width = sum(widths)
max_height = max(heights)
#print(total_width, max_height)
new_im = Image.new('RGB', (total_width, max_height))
x_offset = 0
for im in images:
new_im.paste(im, (x_offset,0))
#print(im.size)
x_offset += im.size[0]
#print(x_offset)
new_im.save(img_path+'final_result_image.jpg')
Here is the combined image:
The third column has a pixel highlighted.
Here is the zoomed in part with the problem.
The JPEG format is lossy - it is allowed to change your pixels to make the file smaller. If your image is a conventional photo of a real-life scene, this doesn't normally matter. If your data is a blocky, computer-generated image, or a set of classes from a classification process, it can go horribly wrong if you use JPEG.
So, the answer is to use PNG (or potentially TIFF) format for images that need to be lossless.

Opencv findCountours function

I'm trying to learn opencv. Online i found that, with opencv, I can obtain the contours of some image. So i tried that. Here is the script:
import cv2
import numpy as np
def getC(imagine):
global imgContour
c,h = cv2.findContours(imagine,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
for cnt in c:
a = cv2.contourArea(cnt)
print(area)
if area>500:
cv2.drawContour(imgContour,cnt,-1,(255,0,0),3)
img = cv2.imread("a3.jpg")
imgContour = img.copy()
imgG = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgB = cv2.GaussianBlur(imgG,(7,7),1)
imgC = cv2.Canny(imgB,50,50)
getC(imgContour)
cv2.imshow("",img)
cv2.imshow("g",imgG)
cv2.imshow("b",imgB)
cv2.imshow("l",imgContour)
cv2.waitKey(0)
I think there is a problem with global variabiles, but also with the format. a3.jpg is that image.
I don't now what to do now, and how to resolve the issue.
Thanks for the help
you saved the area as the variable a but used it with the name area you can fix this by changing the variable name a to area
area = cv2.contourArea(cnt)
there is a typo in cv2.drawContour you should write it like that cv2.drawContours
cv2.drawContours method expects the contour you want to draw to be a list of lists so you need to call it like that
cv2.drawContours(imgContour,[cnt],-1,(255,0,0),3)
when you pass the image to the getC method you gave it an image without pre-processing this image and converting it to threshold image using canny so you need to call it like that
getC(imgC)
The Final Script
import cv2
import numpy as np
def getC(imagine):
global imgContour
print(imgContour.shape)
c,h = cv2.findContours(imagine,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
for cnt in c:
area = cv2.contourArea(cnt)
print(area)
if area>500:
cv2.drawContours(imgContour,[cnt],-1,(255,0,0),3)
img = cv2.imread("./a3.jpg")
imgContour = img.copy()
imgG = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgB = cv2.GaussianBlur(imgG,(7,7),1)
imgC = cv2.Canny(imgB,50,50)
getC(imgC)
cv2.imshow("",img)
cv2.imshow("g",imgG)
cv2.imshow("b",imgB)
cv2.imshow("l",imgContour)
cv2.waitKey(0)

How do you change the color of specified pixels in an image?

I want to be able to detect a certain area of pixels based on their RGB values and change them to some other color (not black/white).
I have tried changing these values in the code, but my resulting images always show black pixels replacing the specified locations:
pixelMap[i,j]= (255,255,255)
from PIL import Image
im = Image.open('Bird.jpg')
pixelMap = im.load()
img = Image.new(im.mode, im.size)
pixelsNew = img.load()
for i in range(img.size[0]):
for j in range(img.size[1]):
toup = pixelMap[i,j]
if(int(toup[0]>175) and int(toup[1]<100 and int(toup[2])<100) ):
pixelMap[i,j]= (255,255,255)
else:
pixelsNew[i,j] = pixelMap[i,j]
img.show()
You will find that iterating over images with Python loops is really slow and should get in the habit of using Numpy or optimised OpenCV or skimage code.
So, starting with this image:
from PIL import Image
import numpy as np
# Open image
im = Image.open('bird.jpg')
# Make into Numpy array
imnp = np.array(im)
# Make all reddish pixels white
imnp[(imnp[:,:,0]>170) & (imnp[:,:,1]<100) & (imnp[:,:,2]<100)] = [255,255,255]
# Convert back to PIL and save
Image.fromarray(imnp).save('result.jpg')
It looks like a tiny bug:
Instead of: pixelMap[i,j]= (255,255,255)
Use: pixelsNew[i,j] = (255,255,255)

I am unable print colored text

I am unable to print the text in Orange colored.I identified the edges of the image and then printed a text on it.
%matplotlib inline
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('ind_maharashtra.png',0)
edges = cv2.Canny(img,100,20)
cv2.imwrite('Edged_img.jpg',edges)
#plt.subplot(121)
img1 = cv2.imread('Edged_img.jpg',0)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img1,'JAI MAHARASHTRA !!',(70,150), font, 0.7,(255,69,0),2,cv2.LINE_8)
cv2.imshow('Maharashtra Map',img1)
#cv2.imshow('Maharashtra Map',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
The problem is that the image on which you are trying to draw ( the image named img1) is a gray-scale image since the 2nd argument of cv2.imread is 0 in the following line:
img1 = cv2.imread('Edged_img.jpg',0)
You have 2 options to fix this issue. First one is to load the image as a color image as follows:
img1 = cv2.imread('Edged_img.jpg')
Alternatively, if you want your canvas to have a gray-ish look, you can just replicate the single channel to form a 3 channel image as follows:
img1 = cv2.imread('Edged_img.jpg', 0)
img1 = cv2.cvtColor(img1, cv2.COLOR_GRAY2BGR)
You are loading your jpg in grayscale, so you will only be able to write grayscale to img1
OpenCV Imread docs
change this line
img1 = cv2.imread('Edged_img.jpg',0)
to
img1 = cv2.imread('Edged_img.jpg',1)
As you can see from the above linked docs, using these numbers is OK but you are actually setting a flag, so you could use the flag definition to make your code clearer. Coincidentally, if you had used the flags you would likely not have had this issue.
You can change your line to
img1 = cv2.imread('Edged_img', cv2.IMREAD_COLOR)
Look how much clearer, and understandable that is. Especially when you come back to this code/hand it over to another developer in a few months time.

Image converted from a scikit array to PIL photoimage is being distorted

I'm trying to add an image that was processed by scikit-image and scipy to a tkinter gui. To add it to the canvas it needs to be either saved as a png, or converted to a PIL image. However, when I try to use ImageTk's Image.fromarray() it distorts the image a lot. I would prefer not to save it as a png, because it's just an intermediate step for generating data labels.
I tried checking the shapes of the arrays, and they're the same. I tried printing out the images, and the filled_objects is the correct image, while im is distorted. So it's not problem in the Tkinter gui. Also, if I don't use np.asarray() it produces the same output.
def generateCanny(imageName):
#imagename should be a path to the image, created with os path join
img = skimage.io.imread(imageName)
print('orig {}'.format(img.shape))
gray = np.sqrt((img*img).sum(-1))
#converts the image to greyscale
edges = skimage.feature.canny(gray, sigma=3)
fill = scipy.ndimage.binary_fill_holes(edges)
return fill
imageName = os.path.join(imagePath, imageStr)
filled_objects = generateCanny(imageName)
a = np.asarray(filled_objects)
im = PIL.Image.fromarray(a)
Here are the two images, im is on the left and filled_objects is on the right
I would think that you could just convert it easily because filled_objects is just an array, but Image.fromarray() must be doing some processing.
The problem is that fromarray isn't interpreting the boolean array a correctly. If you convert a back to RGB with:
# Extend the array into 3 dimensions, repeating the data:
a = np.repeat(a[...,None],3,axis=2).astype(np.uint8)
# Scale to 0-255:
a = 255*a
im = PIL.Image.fromarray(a)
then im.show() will display the correct image.
Converting the result to NumPy's uint8 will do the trick:
from skimage import data, color, feature, util
import tkinter as tk
import numpy as np
from PIL import ImageTk, Image
from scipy.ndimage import binary_fill_holes
rgb = data.hubble_deep_field()
gray = color.rgb2grey(rgb)
edges = feature.canny(gray, sigma=3)
filled_objects = binary_fill_holes(edges)
img_bool = Image.fromarray(filled_objects)
img_uint8 = Image.fromarray(util.img_as_ubyte(filled_objects))
root = tk.Tk()
photo_bool = ImageTk.PhotoImage(img_bool)
photo_uint8 = ImageTk.PhotoImage(img_uint8)
label_bool = tk.Label(root, image=photo_bool).grid(row=1, column=1)
label_uint8 = tk.Label(root, image=photo_uint8).grid(row=1, column=2)
root.mainloop()

Resources