How do you change just the color of some pixels from an image that are not in a predefined list ?
I tried something like this:
from PIL import Image
picture = Image.open("// location")
imshow (picture)
_colors = [[0, 128, 0], [128, 128, 0], [128, 128, 128], [192, 128, 0], [128, 64, 0], [0, 192, 0], [128, 64, 128], [0, 0, 0]]
width, height = picture.size
for x in range(0, width-1):
for y in range(0, height-1):
current_color = picture.getpixel( (x,y) )
if current_color!= _colors[0] and current_color!= _colors[1] and current_color!= _colors[2] and current_color!= _colors[3] and current_color!= _colors[4] and current_color!= _colors[5] and current_color!= _colors[6] and current_color!= _colors[7]:
picture.putpixel( (x,y), (0, 0, 0))
imshow (picture)
I want to make just some pixels black, but somehow this would return a black image altogether
This line :
if current_color!= _colors[0] and current_color!= _colors[1] and current_color!= _colors[2] and current_color!= _colors[3] and current_color!= _colors[4] and current_color!= _colors[5] and current_color!= _colors[6] and current_color!= _colors[7]:
always returns True, so you iterate over the whole picture, changing it to black. getpixel returns a tuple :
>>> print picture.getpixel((1, 1))
(79, 208, 248)
and you compare it to a list( [0,128,0]). They are not the same:
>>> (1,2,3) == [1,2,3]
False
change colors to a list of tuples rather than a list of lists.
keep the type of pixel data the same and shorten that if statement with an "in"
import Image
filename ="name.jpg"
picture = Image.open(filename, 'r')
_colors = [(0, 128, 0), (128, 128, 0), (128, 128, 128), (192, 128, 0), (128, 64, 0), (0, 192, 0), (128, 64, 128), (0, 0, 0)]
width, height = picture.size
for x in range(0, width):
for y in range(0, height):
current_color = picture.getpixel((x,y))
if current_color in _colors:
picture.putpixel((x,y), (0, 0, 0))
picture.show()
Related
I am doing a simple dectection with images. The original size of the image is 1280x1024 but I go through the 64x64 image.
If the subimage is predicted as 1 then I draw a rectangle, but when the rectangles consecutive I prefer to draw only one.
I make the selection of the rectangles with cv2.dnn.NMSBoxes, cv2.dnn.NMSBoxes return the first box no consecutie. How merge the boxes consecutive?
For example:
imgDst = np.zeros((1024, 1280), dtype='float')
rect = [[448, 0, 512, 64], [512, 0, 576, 64], [576, 0, 640, 64], [576, 64, 640, 128], [128, 192, 192, 256]]
boxes = cv2.dnn.NMSBoxes(bboxes=rects, scores=[.9, .9, .9, .9, .9], score_threshold=.5, nms_threshold=.4)
print (boxes)
rect_def = #Some merge of consecutive.
for r in rects:
rectangle(imgDst, (r[0], r[1]), (r[2], r[3]), (0, 0, 255), 2)
Output:
[[0], [3], [4]]
Based on the output I consider that the rectangles 0, 1, 2 should be joined.
I'm trying to pad a RGB image with magenta (255, 0, 255) color with np.pad. But I'm getting an error when using RGB values as constant_values. For example:
import numpy as np
from scipy.misc import face
import matplotlib.pyplot as plt
def pad_img(img, pad_with):
pad_value = max(img.shape[:-1])
img_padded = np.pad(img,
((0, (pad_value - img.shape[0])), # pad bottom
(0, (pad_value - img.shape[1])), # pad right
(0, 0)), # don't pad channels
mode='constant',
constant_values=pad_with)
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.imshow(img)
ax2.imshow(img_padded)
plt.show()
This works fine (padding with white color):
img = face()
pad_img(img, pad_with=255)
And this not (padding with magenta):
img = face()
pad_img(img, pad_with=(255, 0, 255))
Throwing:
ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (3,) and requested shape (3,2)
I think what you are looking for is:
img = face()
pad_img(img, pad_with=(((255, 0, 255), (255, 0, 255)), ((255, 0, 255), (255, 0, 255)), (0, 0)))
According to numpy doc constant_values is of form:
((before_1, after_1), ... (before_N, after_N))
And I think that is why the error says it gets shape (3,) ((255, 0, 255)) for pad_width while it requests shape (3,2) ((((255, 0, 255), (255, 0, 255)), ((255, 0, 255), (255, 0, 255)), (0, 0)))
I have an array of tuples:
a = [(375, 193)
(364, 113)
(277, 20)
(271, 16)
(52, 106)
(133, 266)
(289, 296)
(372, 282)]
How to draw lines between points in OpenCV?
Here is my code that isn't working:
for index, item in enumerate(a):
print (item[index])
#cv2.line(image, item[index], item[index + 1], [0, 255, 0], 2)
Using draw contours, you can draw the shape all at once.
img = np.zeros([512, 512, 3],np.uint8)
a = np.array([(375, 193), (364, 113), (277, 20), (271, 16), (52, 106), (133, 266), (289, 296), (372, 282)])
cv2.drawContours(img, [a], 0, (255,255,255), 2)
If you don't want the image closed and want to continue how you started:
image = np.zeros([512, 512, 3],np.uint8)
pointsInside = [(375, 193), (364, 113), (277, 20), (271, 16), (52, 106), (133, 266), (289, 296), (372, 282)]
for index, item in enumerate(pointsInside):
if index == len(pointsInside) -1:
break
cv2.line(image, item, pointsInside[index + 1], [0, 255, 0], 2)
Regarding your current code, it looks like you are trying to access the next point by indexing the current point. You need to check for the next point in the original array.
A more Pythonic way of doing the second version would be:
for point1, point2 in zip(a, a[1:]):
cv2.line(image, point1, point2, [0, 255, 0], 2)
If you just want to draw lines, how about cv2.polylines? cv2.drawContours would be preferred when you already have a contours object.
cv2.polylines(image,
a,
isClosed = False,
color = (0,255,0),
thickness = 3,
linetype = cv2.LINE_AA)
I am using the Semantic Segmentation network (SegNet). I am trying to reduce the number of classes and thus rearranging the network.
Therefore, I am also changing the color-coding of the predictions as well. My problem is I don't get the intended colors in the output image.
For e.g.
pascal_palette = np.array([(0, 0, 0),
(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
(0, 0, 128), (0, 128, 0), (0, 0, 0), (0, 0, 0), (128, 0, 0),
(0, 0, 0), (0, 0, 0)
], dtype=np.uint8)
The above line gives perfect results for the three classes as the pixels are only in 1 channel.
The output is as below:
However, if I modify the line and add values to different channels it gives weird output. The output is attached below:
pascal_palette = np.array([(0, 0, 0),
(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
(0, 0, 128), (124, 252, 0), (0, 0, 0), (0, 0, 0), (128, 0, 0),
(0, 0, 0), (0, 0, 0)
], dtype=np.uint8)
Changed the color code to (124, 252, 0). The code should be for lawn green color. I also checked it on a website like RBG codes
What am I missing here? Any explanation will be helpful.
Prediciton code:
prob = model.predict(net_in)[0]
# Reshape to 2d here since the networks outputs a flat array per channel
prob_edge = np.sqrt(prob.shape[0]).astype(np.int)
prob = prob.reshape((prob_edge, prob_edge, 13))
# Upsample
if args.zoom > 1:
prob = interp_map(prob, args.zoom, image_size[1], image_size[0])
# Recover the most likely prediction (actual segment class)
prediction = np.argmax(prob, axis=2)
# Apply the color palette to the segmented image
color_image = np.array(pascal_palette)[prediction.ravel()].reshape(
prediction.shape + (3,))
print('Saving results to: ', args.output_path)
with open(args.output_path, 'wb') as out_file:
Image.fromarray(np.multiply(color_image,255)).save(out_file)
PS. I have used same model for predictions in both case
The problem is very probably in np.multiply(color_image,255).
As you created a pallete already with values from 0 to 255 and you're simply gathering values from this pallete, you don't need to multiply it by 255.
Use simply Image.fromarray(color_image).save(out_file).
I know how to map a number to a color from this post: Map values to colors in matplotlib
But I don't know how to decode the mapping to get my original color, assuming this is a one-to-one mapping, which it must be I figure.
I am encoding an image for visualization purposes, but I need to be able to decode it and read the original data values.
For reference, here are the Colormap docs: http://matplotlib.org/api/cm_api.html
Here's my try to the main answer below, which still isn't working right.
from PIL import Image
import numpy as np
import matplotlib
import matplotlib.cm as cm
values = [670, 894, 582, 103, 786, 348, 972, 718, 356, 692]
minima = 103
maxima = 972
norm = matplotlib.colors.Normalize(vmin=minima, vmax=maxima, clip=True)
mapper = cm.ScalarMappable(norm=norm, cmap=cm.gist_rainbow_r)
c = []
for i in range(10):
c.append(mapper.to_rgba(values[i], bytes=True))
print(c) # [(75, 255, 0, 255), (255, 77, 0, 255), (0, 255, 64, 255), (255, 0, 191, 255), (255, 250, 0, 255), (0, 72, 255, 255), (255, 0, 40, 255), (151, 255, 0, 255), (0, 83, 255, 255), (108, 255, 0, 255)]
def get_value_from_cm(color, cmap, colrange):
# color = matplotlib.colors.to_rgba(color)
r = np.linspace(colrange[0], colrange[1], 10) # there are 10 values
norm = matplotlib.colors.Normalize(colrange[0], colrange[1])
mapvals = cmap(norm(r))[:, :4] # there are 4 channels: r,g,b,a
distance = np.sum((mapvals - color) ** 2, axis=1)
return r[np.argmin(distance)]
decoded_colors = []
for i in range(10):
decoded_colors.append(get_value_from_cm(c[i], cm.gist_rainbow_r, colrange=[minima, maxima]))
print(decoded_colors) # [778.88888888888891, 778.88888888888891, 489.22222222222223, 103.0, 778.88888888888891, 392.66666666666669, 103.0, 778.88888888888891, 392.66666666666669, 778.88888888888891]
Inverting the colormapping is possible, if
(a) you know the data range it is mapping and
(b) if you know the colormap that has been used, and
(c) if the colormap is unambiguous.
The following function would return the value given a color, a colormap and the range over which the colormap has been used.
import numpy as np
import matplotlib.colors
import matplotlib.pyplot as plt
def get_value_from_cm(color, cmap, colrange=[0.,1.]):
color=matplotlib.colors.to_rgb(color)
r = np.linspace(colrange[0],colrange[1], 256)
norm = matplotlib.colors.Normalize(colrange[0],colrange[1])
mapvals = cmap(norm(r))[:,:3]
distance = np.sum((mapvals - color)**2, axis=1)
return r[np.argmin(distance)]
b = get_value_from_cm(plt.cm.coolwarm(0.5), plt.cm.coolwarm, [0.,1.])
c = get_value_from_cm(np.array([1,0,0]), plt.cm.coolwarm)
print b # 0.501960784314
print plt.cm.coolwarm(b)
# (0.86742763508627452, 0.86437659977254899, 0.86260246201960789, 1.0)
print plt.cm.coolwarm(0.5)
#(0.86742763508627452, 0.86437659977254899, 0.86260246201960789, 1.0)
Note that this method involves an error, so you only get the closest value from the colormap and not the value that has initially been used to create the color from the map.
In the updated code from the question, you have the color defined as integers between 0 and 255 for each channel. You therefore need to first map those to the range 0 to 1.
from PIL import Image
import numpy as np
import matplotlib
import matplotlib.cm as cm
values = [670, 894, 582, 103, 786, 348, 972, 718, 356, 692]
minima = 103
maxima = 972
norm = matplotlib.colors.Normalize(vmin=minima, vmax=maxima, clip=True)
mapper = cm.ScalarMappable(norm=norm, cmap=cm.gist_rainbow_r)
c = []
for i in range(10):
c.append(mapper.to_rgba(values[i], bytes=True))
print(c) # [(75, 255, 0, 255), (255, 77, 0, 255), (0, 255, 64, 255), (255, 0, 191, 255), (255, 250, 0, 255), (0, 72, 255, 255), (255, 0, 40, 255), (151, 255, 0, 255), (0, 83, 255, 255), (108, 255, 0, 255)]
def get_value_from_cm(color, cmap, colrange):
color = np.array(color)/255.
r = np.linspace(colrange[0], colrange[1], 256)
norm = matplotlib.colors.Normalize(colrange[0], colrange[1])
mapvals = cmap(norm(r))[:, :4] # there are 4 channels: r,g,b,a
distance = np.sum((mapvals - color) ** 2, axis=1)
return r[np.argmin(distance)]
decoded_colors = []
for i in range(10):
decoded_colors.append(get_value_from_cm(c[i], cm.gist_rainbow_r, colrange=[minima, maxima]))
print(decoded_colors)