How to remove blue from an image using opencv? - python

I have this map with a blue square I would like to remove:
I've tried using open cv to do so but I've been wildly unsuccessful. I used the solution here to no avail. The output looks exactly like the original. The blue square is the one intersecting with the red one at the bottom right. That's what I want to remove.
Here is my latest attempt:
from PIL import Image
import PIL.ImageOps
import numpy as np
from skimage.io import imsave
import cv2
in_path = 'map.jpeg'
out_path = 'new_output.jpeg'
Image = cv2.imread(in_path)
Image2 = np.array(Image, copy=True)
white_px = np.asarray([0, 0, 255])
black_px = np.asarray([255 , 255 , 255 ])
(row, col, _) = Image.shape
for r in range(row):
for c in range(col):
px = Image[r][c]
if all(px == white_px):
Image2[r][c] = black_px
imsave(out_path, Image2)

I made a test image like this:
magick -size 400x200 gradient:magenta-lime -stroke blue -fill none +antialias -draw "rectangle 10,10 390,190" a.png
Now there are a couple of ways you can approach this. You could replace all blue pixels with white:
import numpy as np
import cv2
# Load image
filename = 'a.png'
im = cv2.imread(filename)
# Method 1: Simplistic overpaint blue with white
im[np.all(im == (255, 0, 0), axis=-1)] = np.uint8([255,255,255])
cv2.imwrite('result-white.png', im)
Or, maybe slightly better suited to the upper half of this image, replace blue with magenta:
Or you could "inpaint" - what Photoshop calls "Content Aware Fill" - which means that replacement pixels are guesstimated using the surrounding area:
# Method 2: Inpaint - or Photoshop "Content Aware Fill"
im = cv2.imread(filename)
# Make mask of blue pixels - True where blue, False elsewhere
mask = np.all(im == (255, 0, 0), axis=-1)
# Inpaint white areas and save
# Options are: cv2.INPAINT_TELEA or cv2.INPAINT_NS
result = cv2.inpaint(im,np.uint8(mask)*255,3,cv2.INPAINT_TELEA)
cv2.imwrite('result-TELEA.png',result)
result = cv2.inpaint(im,np.uint8(mask)*255,3,cv2.INPAINT_NS)
cv2.imwrite('result-NAVIER-STOKES.png',result)
That gives this result:

just assign blue channel to zeros
src = cv2.imread('D:/original.png', cv2.IMREAD_UNCHANGED)
src[:,:,0] = np.zeros([src.shape[0], src.shape[1]])
cv2.imwrite('D:/no-blue-channel.png',src)

Related

is it possible to change colored areas into black and white where other areas remain same?

I do working in image processing using opencv package. In my image,want to invert the colored regions into binary color where else not affecting other pixels.
For explanation in this image i wish to change the blue regions into white and inside texts has to be in black colors by not affecting below regions.
import cv2
import numpy as np
img = cv2.imread('A.jpg')
img=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
w=img.shape[1]
m=np.mean(img, axis=1)
m=m>150
mask_img=np.repeat(m, w)
mask_img.shape=img.shape
a=255-img^(255*mask_img)
cv2.imwrite('A_out.jpg', a)
convert it to rgb value array
rgbVals = getRGBValues(im) # no idea how to do this offhand
for row in range(20): # first 20 px
for col,px in enumerate(rgbVals[row]):
rgbVals[row][col] = (255,255,255) if rgbVals[row][col] != (255,255,255) else (0,0,0)
new_im = rgbVals_to_image(rgbVals)
here it is more exact
>>> from scipy import misc
>>> a = misc.imread("D:\\Downloads\\pic.jpg") # the image from your post
>>> #get the white(ish) cells
... mask = (a[0:20,:,0] > 160) & (a[0:20,:,1] > 160) & (a[0:20,:,1] > 160)
>>> a[0:20][mask] = [0,0,0] # make the white cells black
>>> a[0:20][~mask] = [255,255,255] # make the rest white
>>> misc.imsave('out.jpg',a)
The output isnt perfect :/ (all the 0:20 means we are just looking at the first 20 rows of pixels )

change white background to a specifc color

Hello I am trying to give a black and white photo a colored background, first I convert pixels to either white or black then I would like to replace the white with a specific color (148,105,39) then save it.
here is the image I am working on
here is my function so far it doesn't change the color (also doesn't give error)
def binary_image(img):
gray = img.convert('L')
bw = gray.point(lambda x: 0 if x<128 else 255, '1')
img = bw.convert('RGBA')
pixdata = img.load()
for y in range(img.size[1]):
for x in range(img.size[0]):
if pixdata[x, y] == (255, 255, 255, 255): #white color
pixdata[x, y] = (148,105,39,255)
return full_4
img=Image.open('logo.jpg')
bg_fps = binary_image(img)
bg_fps.show()
You really, really want to avoid for loops with PIL images, try to use Numpy and it will be easier to write and faster to run.
I would do it like this:
import numpy as np
from PIL import image
# Load image and ensure greyscale
im = Image.open('U6IhW.jpg').convert('L')
# Make Numpy version for fast, efficient access
npim = np.array(im)
# Make solid black RGB output image, same width and height but 3 channels (RGB)
res = np.zeros((npim.shape[0],npim.shape[1],3), dtype=np.uint8)
# Anywhere the grey image is >127, make result image new colour
res[npim>127] = [148,105,39]
# Convert back to PIL Image and save to disk
Image.fromarray(res).save('result.png')
You could equally use PIL's ImageOps.colorize() like this:
from PIL import Image, ImageOps
im = Image.open('U6IhW.jpg').convert('L')
# Threshold to pure black and white
bw = im.point(lambda x: 0 if x<128 else 255)
# Colorize
result = ImageOps.colorize(bw, (0,0,0), (148,105,39))

how to speed up finding which image pixel color is not in a given color list

Purpose: I would like to speed up the process of finding which image pixel value does not contain one of the colors in a given RGB colors table and to map them to another image with _mistakes.png suffix.
Using two for loops to process each pixel individually considering the large size of the image takes long.
import glob
import numpy as np
import os
import cv2
import os.path
# the given list of defined RGB colors.
CLASSES = {
0: [0, 0, 0],
1:[255, 0, 0],
2:[0, 0, 255],
3:[0, 255, 0],
4:[50, 255, 50],
5:[100, 255, 100]
}
for image_path in glob.glob("*.png"):
name = os.path.split(image_path)[-1]
_name = os.path.splitext(name)[0]
img = cv2.imread(image_path)
img_height, img_width, _ = img.shape
img_mistakes = np.zeros((img.shape))
color_codes = np.array(list(CLASSES.values()))
# the following two for loops take so long.
for row in range(img_height):
for col in range(img_width):
if not (img[row,col] == color_codes).all(1).any():
img_mistakes[row, col] = [200, 200, 200] # a chosen color
cv2.imwrite(_name + '_mistakes' + '.png', img_mistakes)
There is probably an even faster way than this , but it's a start! My money is on #divakar to know it - hint, hint ;-)
#!/usr/local/bin/python3
import numpy as np
import cv2
# Open image into numpy array
im=cv2.imread('start.png')
# Work out how one pixel of each colour we are looking for looks
black = [0,0,0]
blue = [255,0,0]
red = [0,0,255]
green = [0,255,0]
# Find all pixels where the 3 RGB values match the sought colour
blacks = np.all(im==black, axis=2)
blues = np.all(im==blue , axis=2)
reds = np.all(im==red , axis=2)
greens = np.all(im==green, axis=2)
# Make empty (black) output array same size as input image
mistakes = np.zeros_like(im)
# Make anything not matching any of our colours into [200,200,200]
mistakes[~(blacks | blues | reds | greens)] = [200,200,200]
# Save result
cv2.imwrite("result.png",mistakes)
start.png
Result:

Applying a coloured overlay to an image in either PIL or Imagemagik

I am a complete novice to image processing, and I am guessing this is quite easy to do, but I just don't know the terminology.
Basically, I have a black and white image, I simply want to apply a colored overlay to the image, so that I have got the image overlayed with blue green red and yellow like the images shown below (which actually I can't show because I don't have enough reputation to do so - grrrrrr). Imagine I have a physical image, and a green/red/blue/yellow overlay, which I place on top of the image.
Ideally, I would like to do this using Python PIL but I would be just as happy to do it using ImageMagik, but either way, I need to be able to script the process as I have 100 or so images that I need to carry out the process on.
EDIT: As mentioned by Matt in the comments, this functionality is now available in skimage.color.label2rgb.
In the latest development version, we've also introduced a saturation parameter, which allows you to add overlays to color images.
Here's a code snippet that shows how to use scikit-image to overlay colors on a grey-level image. The idea is to convert both images to the HSV color space, and then to replace the hue and saturation values of the grey-level image with those of the color mask.
from skimage import data, color, io, img_as_float
import numpy as np
import matplotlib.pyplot as plt
alpha = 0.6
img = img_as_float(data.camera())
rows, cols = img.shape
# Construct a colour image to superimpose
color_mask = np.zeros((rows, cols, 3))
color_mask[30:140, 30:140] = [1, 0, 0] # Red block
color_mask[170:270, 40:120] = [0, 1, 0] # Green block
color_mask[200:350, 200:350] = [0, 0, 1] # Blue block
# Construct RGB version of grey-level image
img_color = np.dstack((img, img, img))
# Convert the input image and color mask to Hue Saturation Value (HSV)
# colorspace
img_hsv = color.rgb2hsv(img_color)
color_mask_hsv = color.rgb2hsv(color_mask)
# Replace the hue and saturation of the original image
# with that of the color mask
img_hsv[..., 0] = color_mask_hsv[..., 0]
img_hsv[..., 1] = color_mask_hsv[..., 1] * alpha
img_masked = color.hsv2rgb(img_hsv)
# Display the output
f, (ax0, ax1, ax2) = plt.subplots(1, 3,
subplot_kw={'xticks': [], 'yticks': []})
ax0.imshow(img, cmap=plt.cm.gray)
ax1.imshow(color_mask)
ax2.imshow(img_masked)
plt.show()
Here's the output:
I ended up finding an answer to this using PIL, basically creating a new image with a block colour, and then compositing the original image, with this new image, using a mask that defines a transparent alpha layer. Code below (adapted to convert every image in a folder called data, outputting into a folder called output):
from PIL import Image
import os
dataFiles = os.listdir('data/')
for filename in dataFiles:
#strip off the file extension
name = os.path.splitext(filename)[0]
bw = Image.open('data/%s' %(filename,))
#create the coloured overlays
red = Image.new('RGB',bw.size,(255,0,0))
green = Image.new('RGB',bw.size,(0,255,0))
blue = Image.new('RGB',bw.size,(0,0,255))
yellow = Image.new('RGB',bw.size,(255,255,0))
#create a mask using RGBA to define an alpha channel to make the overlay transparent
mask = Image.new('RGBA',bw.size,(0,0,0,123))
Image.composite(bw,red,mask).convert('RGB').save('output/%sr.bmp' % (name,))
Image.composite(bw,green,mask).convert('RGB').save('output/%sg.bmp' % (name,))
Image.composite(bw,blue,mask).convert('RGB').save('output/%sb.bmp' % (name,))
Image.composite(bw,yellow,mask).convert('RGB').save('output/%sy.bmp' % (name,))
Can't post the output images unfortunately due to lack of rep.
See my gist https://gist.github.com/Puriney/8f89b43d96ddcaf0f560150d2ff8297e
Core function via opencv is described as below.
def mask_color_img(img, mask, color=[0, 255, 255], alpha=0.3):
'''
img: cv2 image
mask: bool or np.where
color: BGR triplet [_, _, _]. Default: [0, 255, 255] is yellow.
alpha: float [0, 1].
Ref: http://www.pyimagesearch.com/2016/03/07/transparent-overlays-with-opencv/
'''
out = img.copy()
img_layer = img.copy()
img_layer[mask] = color
out = cv2.addWeighted(img_layer, alpha, out, 1 - alpha, 0, out)
return(out)
Add colored and transparent overlay on either RGB or gray image can work:

Python: PIL replace a single RGBA color

I have already taken a look at this question: SO question and seem to have implemented a very similar technique for replacing a single color including the alpha values:
c = Image.open(f)
c = c.convert("RGBA")
w, h = c.size
cnt = 0
for px in c.getdata():
c.putpixel((int(cnt % w), int(cnt / w)), (255, 0, 0, px[3]))
cnt += 1
However, this is very slow. I found this recipe out on the interwebs, but have not had success using it thus far.
What I am trying to do is take various PNG images that consist of a single color, white. Each pixel is 100% white with various alpha values, including alpha = 0. What I want to do is basically colorize the image with a new set color, for instance #ff0000<00-ff>. SO my starting and resulting images would look like this where the left side is my starting image and the right is my ending image (NOTE: background has been changed to a light gray so you can see it since it is actually transparent and you wouldn't be able to see the dots on the left.)
Any better way to do this?
If you have numpy, it provides a much, much faster way to operate on PIL images.
E.g.:
import Image
import numpy as np
im = Image.open('test.png')
im = im.convert('RGBA')
data = np.array(im) # "data" is a height x width x 4 numpy array
red, green, blue, alpha = data.T # Temporarily unpack the bands for readability
# Replace white with red... (leaves alpha values alone...)
white_areas = (red == 255) & (blue == 255) & (green == 255)
data[..., :-1][white_areas.T] = (255, 0, 0) # Transpose back needed
im2 = Image.fromarray(data)
im2.show()
Edit: It's a slow Monday, so I figured I'd add a couple of examples:
Just to show that it's leaving the alpha values alone, here's the results for a version of your example image with a radial gradient applied to the alpha channel:
Original:
Result:
Try this , in this sample we set the color to black if color is not white .
#!/usr/bin/python
from PIL import Image
import sys
img = Image.open(sys.argv[1])
img = img.convert("RGBA")
pixdata = img.load()
# Clean the background noise, if color != white, then set to black.
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y] == (255, 255, 255, 255):
pixdata[x, y] = (0, 0, 0, 255)
you can use color picker in gimp to absorb the color and see that's rgba color
The Pythonware PIL online book chapter for the Image module stipulates that putpixel() is slow and suggests that it can be sped up by inlining. Or depending on PIL version, using load() instead.

Categories