How to remove CT bed/shadows in a CT image with python? - python

I am working with 3D CT images and trying to remove the lines from the bed.
A slice from the original Image:
Following is my code to generate the mask:
segmentation = morphology.dilation(image_norm, np.ones((1, 1, 1)))
labels, label_nb = ndimage.label(segmentation)
label_count = np.bincount(labels.ravel().astype(int))
label_count[0] = 0
mask = labels == label_count.argmax()
mask = morphology.dilation(mask, np.ones((40, 40, 40)))
mask = ndimage.morphology.binary_fill_holes(mask)
mask = morphology.dilation(mask, np.ones((1, 1, 1)))
This results in the following image:
As you can see, in the above image the CT scan as distorted as well.
If I change: mask = morphology.dilation(mask, np.ones((40, 40, 40))) to mask = morphology.dilation(mask, np.ones((100, 100, 100))), the resulting image is as follows:
How can I remove only the two lines under the image without changing the image area? Any help is appreciated.

You've probably found another solution by now. Regardless, I've seen similar CT processing questions on SO, and figured it would be helpful to demonstrate a Scikit-Image solution. Here's the end result.
Here's the code to produce the above images.
from skimage import io, filters, color, morphology
import matplotlib.pyplot as plt
import numpy as np
image = color.rgba2rgb(
io.imread("ctimage.png")[9:-23,32:-9]
)
gray = color.rgb2gray(image)
tgray = gray > filters.threshold_otsu(gray)
keep_mask = morphology.remove_small_objects(tgray,min_size=463)
keep_mask = morphology.remove_small_holes(keep_mask)
maskedimg = np.einsum('ijk,ij->ijk',image,keep_mask)
fig,axes = plt.subplots(ncols=3)
image_list = [image,keep_mask,maskedimg]
title_list = ["Original","Mask","Imgage w/mask"]
for i,ax in enumerate(axes):
ax.imshow(image_list[i])
ax.set_title(title_list[i])
ax.axis("off")
fig.tight_layout()
Notes on code
image = color.rgba2rgb(
io.imread("ctimage.png")[9:-23,32:-9]
)
gray = color.rgb2gray(image)
The image saved as RGBA when I loaded it from SO. It needs to be in grayscale for use in the threshold function.
Your image might already by in grayscale.
Also, the downloaded image showed axis markings. That's why I've trimmed the image.
maskedimg = np.einsum('ijk,ij->ijk',image,keep_mask)
I wanted to apply keep_mask to every channel of the RGB image. The mask is a 2D array, and the image is a 3D array. I referenced this previous question in order to apply the mask to the image.

Related

How to remove reflection from sunglasses and change the color of lenses in sunglasses

I am working on a project where I have to remove the reflection on sunglasses and also change the color of lenses. So, I tried to detect the dominant color in the image (sunglass lenses) and then tried to replace that color by another color using OpenCV.
But the code is not working properly. So, Please help me.
Here is the result that have obtained.
Here is the code that I have used to crop part of image(lenses) then detect the dominant color.
import cv2
from google.colab.patches import cv2_imshow
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np
from collections import Counter
from skimage.color import rgb2lab, deltaE_cie76
import os
img = cv2.imread('originalimage.jpg')
cropped_lens2 = img[556:2045, 2000:3177]
image = cv2.cvtColor(cropped_lens2, cv2.COLOR_BGR2RGB)
modified_image = cv2.resize(image, (600, 400), interpolation = cv2.INTER_AREA)
cv2_imshow(modified_image)
modified_image = modified_image.reshape(modified_image.shape[0]*modified_image.shape[1], 3)
number_of_colors=2
clf = KMeans(n_clusters = number_of_colors)
labels = clf.fit_predict(modified_image)
counts = Counter(labels)
center_colors = clf.cluster_centers_
ordered_colors = [center_colors[i] for i in counts.keys()]
hex_colors = [RGB2HEX(ordered_colors[i]) for i in counts.keys()]
rgb_colors = [ordered_colors[i] for i in counts.keys()]
plt.figure(figsize = (8, 6))
plt.pie(counts.values(), labels = hex_colors, colors = hex_colors)
Input Image and output images are as shown below.
Original image
Cropped image
color range output
Code that I have used to replace range of colors by a single color.
you can also replace it by gradient colors.
from PIL import Image
img = Image.open("old_Test/DSC-0296 fold.jpg")
img = img.convert("RGB")
datas = img.getdata()
new_image_data = []
for item in datas:
if item[0] in list(range(0, 80)):
new_image_data.append((255, 204, 100))
else:
new_image_data.append(item)
img.putdata(new_image_data)
img
output image after replacing colors
Change color of any image using OpenCV:
Based on your comment maybe this can help you
You can make a mask from the part that needs to be changed.
def changeColor(im, msk, hue=130):
h, s, v = cv2.split(cv2.cvtColor(im.copy(), cv2.COLOR_BGR2HSV))
h[np.where(msk == 0)] = hue
return cv2.cvtColor(cv2.merge([h, s, v]), cv2.COLOR_HSV2BGR)
Of these 6 images, the top left is the main image I drew with a graphic software. The image at the bottom left is a mask that tells the algorithm where the image should change. Apart from these 2 images, the other 4 are function tests.

Denoising a photo with Python

I have the following image which is a scanned copy of an old book. I want to remove the noise in the background (which is a bit reddish) that is coming due to the scanning of the old photo.
Update:
After applying opencv, following the parameter settings in opencv doc, I am getting the following output.
Please help fixing this.
The code that I am using:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def display_image_in_actual_size(im_data):
dpi = 80
height, width, depth = im_data.shape
# What size does the figure need to be in inches to fit the image?
figsize = width / float(dpi), height / float(dpi)
# Create a figure of the right size with one axes that takes up the full figure
fig = plt.figure(figsize=figsize)
ax = fig.add_axes([0, 0, 1, 1])
# Hide spines, ticks, etc.
ax.axis('off')
# Display the image.
ax.imshow(im_data, cmap='gray')
plt.show()
img = cv2.imread('scan03.jpg')
dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)
display_image_in_actual_size(img)
display_image_in_actual_size(dst)
The color of some pixels which has near threshold pixel values will be affected, but that depends on the task, here is one solution that you might adjust the threshold to a value that suits your task, also you might remove the median filter, or reduce the sigma value(5) if it affects the text badly, you might have some undesired noise, but the text will be readable.
import numpy as np
import matplotlib.pyplot as plt
import cv2
# Read Image
img = cv2.imread('input.jpg')
# BGR --> RGB
RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# BGR --> Gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Set thresholds
th_white = 210
th_black = 85
# copy original gray
mask_white = gray.copy()
mask_black = gray.copy()
# Thresholding
mask_white[mask_white<th_white] = 0
mask_black[mask_black<th_black] = 0
mask_white[mask_white>=th_white] = 255
mask_black[mask_black>=th_black] = 255
# Median Filtering (you can remove if the text is not readable)
median_white = cv2.medianBlur(mask_white,5)
median_black = cv2.medianBlur(mask_black,5)
# Mask 3 channels
mask_white_3 = np.stack([median_white, median_white, median_white], axis=2)
mask_black_3 = np.stack([median_black, median_black, median_black], axis=2)
# Masking the image(in RGB)
result1 = np.maximum(mask_white_3, RGB)
result2 = np.minimum(mask_black_3, result1)
# Visualize the results
plt.imshow(result2)
plt.axis('off')
plt.show()
opencv library has couple of denoisong functions.
You can find reading with examples here

How do I crop an image based on custom mask in python?

Below I have attached two images. I want the first image to be cropped in a heart shape according to the mask image (2nd image).
I searched for solutions but I was not able to get the simple and easier way to do this. Kindly help me with the solution.
2 images:
Image to be cropped:
Mask image:
Let's start by loading the temple image from sklearn:
from sklearn.datasets import load_sample_images
dataset = load_sample_images()
temple = dataset.images[0]
plt.imshow(temple)
Since, we need to use the second image as mask, we must do a binary thresholding operation. This will create a black and white masked image, which we can then use to mask the former image.
from matplotlib.pyplot import imread
heart = imread(r'path_to_im\heart.jpg', cv2.IMREAD_GRAYSCALE)
_, mask = cv2.threshold(heart, thresh=180, maxval=255, type=cv2.THRESH_BINARY)
We can now trim the image so its dimensions are compatible with the temple image:
temple_x, temple_y, _ = temple.shape
heart_x, heart_y = mask.shape
x_heart = min(temple_x, heart_x)
x_half_heart = mask.shape[0]//2
heart_mask = mask[x_half_heart-x_heart//2 : x_half_heart+x_heart//2+1, :temple_y]
plt.imshow(heart_mask, cmap='Greys_r')
Now we have to slice the image that we want to mask, to fit the dimensions of the actual mask. Another shape would have been to resize the mask, which is doable, but we'd then end up with a distorted heart image. To apply the mask, we have cv2.bitwise_and:
temple_width_half = temple.shape[1]//2
temple_to_mask = temple[:,temple_width_half-x_half_heart:temple_width_half+x_half_heart]
masked = cv2.bitwise_and(temple_to_mask,temple_to_mask,mask = heart_mask)
plt.imshow(masked)
If you want to instead make the masked (black) region transparent:
tmp = cv2.cvtColor(masked, cv2.COLOR_BGR2GRAY)
_,alpha = cv2.threshold(tmp,0,255,cv2.THRESH_BINARY)
b, g, r = cv2.split(masked)
rgba = [b,g,r, alpha]
masked_tr = cv2.merge(rgba,4)
plt.axis('off')
plt.imshow(dst)
Since I am on a remote server, cv2.imshow doesnt work for me. I imported plt.
This code does what you are looking for:
import cv2
import matplotlib.pyplot as plt
img_org = cv2.imread('~/temple.jpg')
img_mask = cv2.imread('~/heart.jpg')
##Resizing images
img_org = cv2.resize(img_org, (400,400), interpolation = cv2.INTER_AREA)
img_mask = cv2.resize(img_mask, (400,400), interpolation = cv2.INTER_AREA)
for h in range(len(img_mask)):
for w in range(len(img_mask)):
if img_mask[h][w][0] == 0:
for i in range(3):
img_org[h][w][i] = 0
else:
continue
plt.imshow(img_org)

trouble applying filter to a `.png` image, although the filter works fine with a `.tif` image

I have 2 images that I want to apply filter to. The first one is .tif black and white and the other .png black and white but (I suppose) with a color channel.
Here is a link to my github for the images:
here are the 2 original images
cameraman.tif:
einstein.png:
I have no problem applying the filter to the first image cameraman.tif with the following result:
Regarding the second image (einstein.png), I have first to read it in a grayscale format to be able to apply the filter at all. But when I apply the filter the image does not change:
Here is the code:
import matplotlib.image as mpimg
import numpy as np
from skimage import color
from skimage import io
img = plt.imread('cameraman.tif')
einstein_bw = color.rgb2gray(io.imread('einstein.png'))
def contrast(img, size=5):
filtered = np.zeros_like(img)
H, W = img.shape
for i in range(size//2, H-size//2):
for j in range(size//2, W-size//2):
patch = img[i-size//2:i+size//2+1, j-size//2:j+size//2+1]
filtered[i,j] = 1.2*img[i, j] - 1/(size**2)*np.mean(patch)
return filtered
contrasted = contrast(img, 3)
plt.imshow(contrasted, cmap='gray')
plt.savefig('cameraman_contrast.png')
contrasted = contrast(einstein_bw, 5)
plt.imshow(contrasted, cmap='gray')
plt.savefig('einstein_contrast.png')

opencv python connectedComponents select component per label

I want to select each component of this image :
In practice, each and every triangle, by its labels. I don't figure out how.
I have this code:
#!/usr/bin/python
import cv2
import numpy as np
img = cv2.imread('invMehs.png', -1)
imGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, imBw = cv2.threshold(imGray, 250, 255, cv2.THRESH_BINARY)
invBwMesh = cv2.bitwise_not(imBw)
Mask = np.ones(imBw.shape, dtype="uint8") * 255
connectivity = 4
output = cv2.connectedComponentsWithStats(imBw, connectivity, cv2.CV_32S)
num_labels = output[0]
labels = output[1]
stats = output[2]
centroids = output[3]
labels = labels + 1
b = ( labels == 1)
cv2.imwrite('tst.jpg',labels[b])
But the image is complety black :S
Thank you very much.
The image you want save (labels[b]) only contains the thin lines (greylevel 1). When saving image using JPEG format, the compression algorithm smooths them, but since they have only 1 greylevel with the background, they are erased. That's why you get a black image
Saving in PNG format do not alter the image labels.
In order to keep all labels for each connected component (0 for the background), the code to write should be :
cv2.imwrite('labels.png',output[1])

Categories