I need to add noise to multiple of coloured images (file format is ppm; source: http://benchmark.ini.rub.de/Dataset/GTSRB_Final_Training_Images.zip) in python. The noised output images should be still in colour.
I tryed the following:
from scipy import misc
import numpy as np
import cv2
import imageio
# Read image ('00000_00030.ppm') from file system
image = misc.imread('./00000/00000_00030.ppm', mode="RGB")
# Add noise to the input image
noised_image = image + 3 * image.std() * np.random.random(image.shape)
# Plot original and noisy images
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
f, axarr = plt.subplots(2, 2)
axarr[0, 0].imshow(image)
axarr[0, 0].set_title('Original image')
axarr[0, 1].imshow(noised_image)
axarr[0, 1].set_title('Noised image')
plt.show()
# Save noised image to file system
saved_image = cv2.imwrite("./noised.ppm", noised_image)
But first of all the problem is that the noised image won't be plotted correctly in jupyter notebook (see figure 1):
figure 1
The second problem is that the RG-channels (Red and Green) were be lost (in saved file):
figure 2
So how can I preserve all RGB colors in noised image?
After searching for a long time I have the solution now - the saved file preserves now all RGB-colours (See Line 8 in following code; see figure 3):
from scipy import misc
import numpy as np
import cv2
import imageio
# Read image ('00000_00030.ppm') from file system
# image = misc.imread('./00000/00000_00030.ppm', mode="RGB")
image = cv2.imread('./00000/00000_00030.ppm',1)
# Add noise to the input image
noised_image = image + 3 * image.std() * np.random.random(image.shape)
# Plot original and noisy images
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
f, axarr = plt.subplots(2, 2)
axarr[0, 0].imshow(image)
axarr[0, 0].set_title('Original image')
axarr[0, 1].imshow(noised_image)
axarr[0, 1].set_title('Noised image')
plt.show()
# Save noised image to file system
saved_image = cv2.imwrite("./noised1.ppm", noised_image)
Figure 3
But the plotted figures are still wrong:
Figure 4
Here is the final Code to add noise to RGB images in python, and plot them correctly:
from scipy import misc
import numpy as np
import cv2
import imageio
# Read image ('00000_00030.ppm') from file system
# image = misc.imread('./00000/00000_00030.ppm', mode="RGB")
image = cv2.imread('./00000/00000_00030.ppm',1)
# Add noise to the input image
noised_image = image + 3 * image.std() * np.random.random(image.shape)
RGB_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Plot original and noisy images
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
f, axarr = plt.subplots(2, 2)
axarr[0, 0].imshow(RGB_image)
axarr[0, 0].set_title('Original image')
axarr[0, 1].imshow(noised_image)
axarr[0, 1].set_title('Noised image')
plt.show()
# Save noised image to file system
saved_image = cv2.imwrite("./noised1.ppm", noised_image)
This will take the pixel values of the given image and start encoding the noise that you give as input to the least significant bits in the pixel. The image output would vary slightly.
def asciiToBin(ascii):
return ''.join(str(bin(ord(byte)))[2:].zfill(8) for byte in ascii)
def hide(img, data, outName):
dataBin = asciiToBin(data)
pixels, mode = list(img.getdata()), img.mode
newPixels = []
for i in range(len(dataBin)):
newPixel = list(pixels[i])
newPixel[i%len(mode)] = setLSB(newPixel[i%len(mode)], dataBin[i])
newPixels.append(tuple(newPixel))
newData = newPixels + pixels[len(newPixels):]
img.putdata(newData)
img.save(outName, "PNG")
def setLSB(target, value):
binary = str(bin(target))[2:]
if binary[-1] != value:
binary = binary[:-1] + value
return int(binary, 2)
Related
Using scikit library I was analysing the defects' area and mean diameter. Here is the code and the respective segmented regions.
import cv2
import numpy as np
from matplotlib import pyplot as plt
from skimage import measure, io, img_as_ubyte
from skimage.color import label2rgb, rgb2gray
img = cv2.imread("F:\py_image_pro\pore.jpg", 0)
scale = 0.086 #1 pixel in microns
from skimage.filters import threshold_otsu
threshold = threshold_otsu(img)
thresholded_img = img < threshold
#plt.imshow(thresholded_img, cmap='gray')
#plt.show()
from skimage.segmentation import clear_border
edge_touching_removed = clear_border(thresholded_img)
label_image = measure.label(edge_touching_removed, connectivity=img.ndim)
#plt.imshow(label_image)
#plt.show()
image_label_overlay = label2rgb(label_image, image=img)
plt.imshow(image_label_overlay)
plt.show()
props = measure.regionprops_table(label_image, img, properties=['label', 'area', 'equivalent_diameter', 'mean_intensity', 'solidity'])
import pandas as pd
df = pd.DataFrame(props)
df = df[df['area'] > 20]
df['area_in_microns'] = df['area'] * (scale**2)
df['equivalent_diameter_microns'] = df['equivalent_diameter'] * (scale)
print(df.head())
Used regionprops to measure the segmented regions. Segmented image
I would like to know if there is any way to display the labels in the output image so that segmented labels' corresponding measurements can be known?
Are you asking whether you can display a colormapped version of the measurements on top of the image? If so, the answer is yes! You can use skimage.util.map_array for this.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from skimage import (
color, data, filters, measure,
morphology, segmentation, util
)
# grab the image
coins = data.coins()
# segment the image; from:
# https://scikit-image.org/docs/dev/auto_examples/segmentation/plot_expand_labels.html
edges = filters.farid(coins)
markers = np.zeros_like(coins)
markers[coins < 30] = 1
markers[coins > 150] = 2
watershed = segmentation.watershed(edges, markers)
segmented_raw = measure.label(watershed == 2)
# remove tiny background objects due to noise
segmented = morphology.remove_small_objects(segmented_raw, 64)
# measure regionprops
table = pd.DataFrame(measure.regionprops_table(
segmented, coins, properties=('label', 'area')
))
# map the labels to measured properties
colored_by_area = util.map_array(
segmented,
np.asarray(table['label']),
np.asarray(table['area']).astype(float),
)
# set 0 to nan, so it appears as transparent in pyplot.imshow
colored_by_area[colored_by_area==0] = np.nan
# display the results
fig, axes = plt.subplots(1, 2, sharex=True, sharey=True)
colored_by_label = color.label2rgb(segmented, image=coins, bg_label=0)
axes[0].imshow(colored_by_label)
axes[0].set_axis_off()
axes[0].set_title('segmentation')
axes[1].imshow(coins, cmap='gray')
axim = axes[1].imshow(colored_by_area, cmap='viridis')
axes[1].set_axis_off()
axes[1].set_title('segment area')
plt.colorbar(axim, ax=axes[1], fraction=0.05, label='area (px)')
plt.show()
I'm scanning a book and now I'm on the final step. I need to crop all squared images with content in the center by white background without saving ratio. Some libs do not support TIFF format.
How can I do that ?
I combined some examples from How do I crop an image on a white background with python?, How do I crop an image on a white background with python? and other questions. It works
from PIL import Image
from skimage import io, img_as_float
import sys
import numpy as np
import glob
import matplotlib.pyplot as plt
filePaths = glob.glob("*.tif")
for filePath in filePaths:
image = img_as_float(io.imread(filePath))
white = np.array([1, 1, 1, 1])
mask = np.abs(image - white).sum(axis=2) < 0.05
coords = np.array(np.nonzero(~mask))
top_left = np.min(coords, axis=1)
bottom_right = np.max(coords, axis=1)
out = image[top_left[0]:bottom_right[0],
top_left[1]:bottom_right[1]]
plt.imshow(out)
# plt.show()
fig = plt.gcf()
for ax in fig.axes:
ax.axis('off')
ax.margins(0,0)
ax.xaxis.set_major_locator(plt.NullLocator())
ax.yaxis.set_major_locator(plt.NullLocator())
fig.savefig(filePath + ".png", dpi=1200, bbox_inches="tight", pad_inches=0)
I came over the following issue: the resize functions of these two libraries behave differently. Here is a small test:
import numpy as np
import PIL
import cv2
from matplotlib import pyplot as plt
img = np.random.randn(10, 10, 3)
SIZE = (5, 5)
img -= img.min()
img /= img.max()
img = (img*255).astype(np.uint8)
# Display the initial image
plt.figure(figsize=(16,9))
plt.imshow(img)
plt.show()
plt.close()
# resize the image in two different ways
img_cv2 = cv2.resize(img, dsize=SIZE, interpolation=cv2.INTER_LINEAR)
img_pil = PIL.Image.fromarray(img).resize(SIZE, resample=PIL.Image.BILINEAR)
# get the difference image and normalize it
diff = np.abs(img_cv2.astype(np.float32) - img_pil)
diff /= diff.max() or 1
# display results
fig, axs = plt.subplots(1, 3, figsize=(16, 9))
axs[0].imshow(img_cv2)
axs[1].imshow(img_pil)
axs[2].imshow(diff)
plt.show()
plt.close()
My question is now: why is this happening? Is the difference in the implementation (I didn't check the code in PIL or OpenCV yet) or am I using the functions in the wrong way?
Here are some example outputs: Input image and Resized images.
I am having a problem converting an image I;16 to JPEG with PIL.
My original image can be found here (as pickle).
The original image comes from a DICOM file.
Here is the code to try:
import pickle
import matplotlib.pyplot as plt
from PIL import Image
ims = pickle.load(open("pixel_array.pickle", "rb"))
img = Image.fromarray(ims)
print(img.mode)
rgb_im = img.convert("RGB")
print(rgb_im.mode)
fig, ax = plt.subplots(figsize=(20, 10))
ax.imshow(rgb_im, cmap=plt.cm.bone)
fig.show()
Unfortunately the image is completely white, while it should be a chest x-ray scan image.
I followed this other stackoverflow question, and with the following
ims = pickle.load(open("pixel_array.pickle", "rb"))
img = Image.fromarray(ims)
print(img.mode)
img.mode = 'I'
rgb_im = img.point(lambda i:i*(1./256)).convert('L')
rgb_im.save('my.jpeg')
fig, ax = plt.subplots(figsize=(20, 10))
ax.imshow(rgb_im, cmap=plt.cm.bone)
fig.show()
I am able to visualise the image, but unfortunately my.jpeg is a black image. Please help!
Your values are 16-bit and need to be reduced to 8-bit for display. You can scale them from their current range of 2,712 (i.e. ims.min()) to 4,328 (i.e. ims.max()) with the following:
from PIL import Image
import numpy as np
import pickle
# Load image
ims = pickle.load(open("pixel_array.pickle", "rb"))
# Normalise to range 0..255
norm = (ims.astype(np.float)-ims.min())*255.0 / (ims.max()-ims.min())
# Save as 8-bit PNG
Image.fromarray(norm.astype(np.uint8)).save('result.png')
I am trying to convert the color map of a contour generated from non-Python application. I tried using Matthias Bussonnier's code available here, but is unable to give me a full conversion. I tried to truncate the color map to give me a full conversion, but again does not give me a complete conversion.
MWE
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as colors
import matplotlib.image as mpimg
from scipy.spatial import cKDTree
import matplotlib
import matplotlib.cm as mplcm
def truncate_colormap(cmap, minval=0.0, maxval=1.0, n=100):
new_cmap = colors.LinearSegmentedColormap.from_list(
'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval, b=maxval),
cmap(np.linspace(minval, maxval, n)))
return new_cmap
cmap = plt.get_cmap('jet')
cmap = truncate_colormap(cmap, 0.1, 0.9)
img = mpimg.imread('./test.png')[:,:,:3]
##interact(sub=(0, 500), d=(0,1,0.05))
def convert(sub=256,d=0.1, cin=cmap, cout='viridis'):
viridis = plt.get_cmap(cout)
jet = plt.get_cmap(cin)
jet256 = colors.makeMappingArray(sub, jet)[:, :3]
K = cKDTree(jet256)
oshape = img.shape
img_data = img.reshape((-1,3))
res = K.query(img_data, distance_upper_bound=d)
indices = res[1]
l = len(jet256)
indices = indices.reshape(oshape[:2])
remapped = indices
indices.max()
mask = (indices == l)
remapped = remapped / (l-1)
mask = np.stack( [mask]*3, axis=-1)
blend = np.where(mask, img, viridis(remapped)[:,:,:3])
fig, ax = plt.subplots()
fig.set_figheight(10)
fig.set_figwidth(10)
ax.imshow(blend)
fig.savefig('viridize.pdf')
convert()
Input image
Output image
How do I get a complete conversion of the color map (jet in this case) to viridis with Python?
As commented, the solution from How I can specify how rainbow color scheme should be converted to grayscale
will work, but with some small modifications.
I.e. you need to apply your target colormap to the values optained from that solution and hence modify the resulting array size to be 3D.
The conditions for this to work are:
You know the colormap that the original image has been produced with (origin_cmap)
All colors in that image are either grey scale (axes, text etc.) or part of that origin_cmap. I.e. there should not be any other line plot or similar in addition in the figure.
The original colormap is unambiguous, i.e. does not contain the same color twice.
The full range of the original colormap has been used to create the input image and the full range of the target colormap will be aimed for. (This condition can be weakend though if needed, by specifying a different norm and/or range)
The following will hence "viridify" a given image.
import numpy as np
import matplotlib.colors
import matplotlib.pyplot as plt
image = plt.imread("https://i.stack.imgur.com/NyLq2.png")
def changecolormap(image, origin_cmap, target_cmap):
r = np.linspace(0,1, 256)
norm = matplotlib.colors.Normalize(0,1)
mapvals = origin_cmap(norm(r))[:,:3]
def get_value_from_cm(color):
color=matplotlib.colors.to_rgb(color)
#if color is already gray scale, dont change it
if np.std(color) < 0.1:
return color
#otherwise return value from colormap
distance = np.sum((mapvals - color)**2, axis=1)
return target_cmap(r[np.argmin(distance)])[:3]
newim = np.zeros_like(image)
for i in range(image.shape[0]):
for j in range(image.shape[1]):
c = image[i,j,:3]
newim[i,j, :3] = get_value_from_cm(c)
return newim
fig, (ax,ax2) = plt.subplots(ncols=2)
ax.imshow(image)
ax2.imshow(changecolormap(image, plt.cm.jet, plt.cm.viridis))
ax.axis("off")
ax2.axis("off")
plt.show()