cv2.MORPH_CLOSE joining letters together - python-3.x

I'm using Opencv to do some morphological operations on an image:
but it joins some of the letters together creating problems when I detect it's contours. For example:
Is there some tweaking I can do wih my code to fix this or will I have to do this a different way?(but it has to be a closing algorithm or function because it is pretty helpful in preprocessing).
My code I am using is as below:
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel)

Here is workable solution:
import numpy as np
import cv2
from matplotlib import pyplot as plt
I = cv2.imread('/home/smile/Downloads/words.jpg',cv2.IMREAD_GRAYSCALE)
_,It = cv2.threshold(I,0.,255,cv2.THRESH_OTSU)
It = cv2.bitwise_not(It)
_,labels = cv2.connectedComponents(I)
result = np.zeros((I.shape[0],I.shape[1],3),np.uint8)
for i in range(labels.min(),labels.max()+1):
mask = cv2.compare(labels,i,cv2.CMP_EQ)
_,ctrs,_ = cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
result = cv2.drawContours(result,ctrs,-1,(0xFF,0,0))
plt.figure()
plt.imshow(result)
plt.axis('off')
During the two first steps the image is binarize and reverse in order to make the letters appear has white over black.
_,It = cv2.threshold(I,0.,255,cv2.THRESH_OTSU)
It = cv2.bitwise_not(It)
Then during the next step each letter become a labelized region.
_,labels = cv2.connectedComponents(I)
The final step consist for each label value to find the area in the image that correspond to it, process the external contour of that area and "draw" it in the output image.
result = np.zeros((I.shape[0],I.shape[1],3),np.uint8)
for i in range(labels.min(),labels.max()+1):
mask = cv2.compare(labels,i,cv2.CMP_EQ)
_,ctrs,_ = cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
result = cv2.drawContours(result,ctrs,-1,(0xFF,0,0)
Hope it helps.

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.

Detecting line-line intersections in an image?

I have an image with some intersecting lines where I need to find the point of intersection. I am using cv2.goodFeaturesToTrack to find strong corners, and working on the assumption that intersections are 'strong' corners so they will get detected. But it's not a sure fire way of getting the intersection points of the two lines. Another approach is that I can get the equations of the lines and calculate the line-line intersection... or any other suggestion.
import matplotlib.pyplot as plt
import cv2
import numpy as np
img = cv2.imread('test_lines.png')
new = img.copy()
#invert
imagem = cv2.bitwise_not(img)
gray = cv2.cvtColor(imagem, cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray, 4, 0.01, 10, blockSize = 5)
corners = np.int0(corners)
for i in corners:
import pdb; pdb.set_trace()
x, y = i.ravel()
cv2.circle(imagem, (x,y),3,255,-1)
plt.imshow(imagem)
cv2.imwrite('hough_img.png',imagem)
How to detect lines in OpenCV?
This answer was helpful in giving me some good results to begin working with. I followed the steps there to get the following result.

compute method of cv2.HOGDescriptor always returns an empty tuple

I am using the code snippet given in here to compute HOG feature of a small image patch. However for the code attached herewith, the variable h, which is supposed to hold HOG feature values, is returning an empty tuple instead. Can anyone please point me to where am I going wrong in the code?
import numpy as np
import cv2
img = cv2.imread('newimg.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gray = cv2.resize(img_gray,(50,50))
hog = cv2.HOGDescriptor()
h = hog.compute(img_gray)
print(h)
The test image is attached
I think there is problem regarding the image size. More specifically image size is smaller as compare to the default window size of hog Descriptor. I recommend resize your image and then use the code snippet attached below.
winSize = (32,32)
blockSize = (16,16)
blockStride = (8,8)
cellSize = (8,8)
nbins = 9
hog = cv2.HOGDescriptor(winSize, blockSize, blockStride, cellSize, nbins)
h = hog.compute(img_gray)
print(h)

Removing noise after doing some preprocessing, so how come it fails? (OpenCV with Python)

So After some image preprocessing I have gotten an image which holds 5 contours
(The image was resized for posting here in stackoverflow):
I'd like to remove all "islands" except for the actual letter,
So at first I tried using cv2.erode and cv2.dilate with all kinds of kernels sizes and it didn't do the job, so I decided to remove by masking all contours except the largest one by this:
_, cnts, _ = cv2.findContours(original, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
I would expect according to the given image there would be 5 contours
areas = []
for contour in cnts:
area = cv2.contourArea(contour)
areas.append(area)
relevant_indexes = list(range(1, len(cnts)))
relevant_indexes.remove(areas.index(max(areas)))
mask = numpy.zeros(eroded.shape).astype(eroded.dtype)
color = 255
for i in relevant_indexes:
cv2.fillPoly(mask, cnts[i], color)
cv2.imwrite("mask.png", mask)
// Trying to mask out the noise
result = cv2.bitwise_xor(orifinal, mask)
cv2.imwrite("result.png", result)
But the mask I get is:
it's not what I would expect, and the left down contour is missing,
can someone PLEASE explain me what am I missing here? And what would be the correct approach for getting rid of those "isolated islands"?
Thank you all!
p.s
The original photo I'm working on:
Solution:
It sounds like you want to mask out the largest connected component (cv-speak for "island").
Here's an opencv/python script to do that:
#!/usr/bin/env python
import cv2
import numpy as np
import console
# load image in grayscale
img = cv2.imread("img.png", 0)
# get all connected components
_, output, stats, _ = cv2.connectedComponentsWithStats(img, connectivity=4)
# get a list of areas for each group label
group_areas = stats[cv2.CC_STAT_AREA]
# get the id of the group with the largest area (ignoring 0, which is the background id)
max_group_id = np.argmax(group_areas[1:]) + 1
# get max_group_id mask and save it as an image
max_group_id_mask = (output == max_group_id).astype(np.uint8) * 255
cv2.imwrite("output.png", max_group_id_mask)
Result:
Here's the result of the above script on your sample image:

Crop an image to smaller size from left to right and top to bottom using OpenCV

I have an image 315x581. I want to crop it in 28x28 from top left to bottom right, then I need to save each 28x28 image in folder.
I could crop just one image from y1=0 to y2=28 and x1=0 to x2=28.
First problem is: I used cv2.imwrite("cropped.jpg", cropped) to save this small image, but It doesn't save it, provided that it works some line above.
Second problem is: How can I write a code which it keeps on cropping the image in 28x28 from left to right and top to bottom and save each subimage.
I used for loop, but I don't know how to complete it.
Thank you so much for any help.
Here this is my code,
import cv2
import numpy as np
from PIL import Image
import PIL.Image
import os
import gzip
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.cm as cm
#%%
image1LL='C:/Users/Tala/Documents/PythonProjects/Poster-OpenCV-MaskXray/CHNCXR_0001_0_LL.jpg'
mask1LL='C:/Users/Tala/Documents/PythonProjects/Poster-OpenCV-MaskXray/CHNCXR_0001_0_threshLL.jpg'
#finalsSave='C:/Users/Tala/Documents/PythonProjects/Poster-OpenCV-MaskXray/Xray Result'
# load the image
img = cv2.imread(image1LL,0)
mask = cv2.imread(mask1LL,0)
# combine foreground+background
final1LL = cv2.bitwise_and(img,img,mask = mask)
cv2.imshow('final1LL',final1LL)
cv2.waitKey(100)
final1LL.size
final1LL.shape
# Save the image
cv2.imwrite('final1LL.jpg',final1LL)
# crop the image using array slices -- it's a NumPy array
# after all!
y1=0
x1=0
for y2 in range(0,580,28):
for x2 in range(0,314,28):
cropped = final1LL[0:28, 0:28]
cv2.imshow('cropped', cropped)
cv2.waitKey(100)
cv2.imwrite("cropped.jpg", cropped)
Your approach is good, but there is some fine tuning required. The following code will help you:
import cv2
filename = 'p1.jpg'
img = cv2.imread(filename, 1)
interval = 100
stride = 100
count = 0
print img.shape
for i in range(0, img.shape[0], interval):
for j in range(0, img.shape[1], interval):
print j
cropped_img = img[j:j + stride, i:i + stride] #--- Notice this part where you have to add the stride as well ---
count += 1
cv2.imwrite('cropped_image_' + str(count) + '_.jpg', cropped_img) #--- Also take note of how you would save all the cropped images by incrementing the count variable ---
cv2.waitKey()
My result:
Original image:
Some of the cropped images:
Cropped image 1
Cropped image 2
Cropped image 3
If you are using it in PyTorch as a deep learning framework, then this task would be quite easy and can be done without the need for any other external image processing libraries such as OpenCV. The below code will convert a single image into a stack of multiple images in a form of PyTorch tensor. If you want to use only images then you need to remove the line "transforms.ToTensor()" and save the "tens" variable in the code as an image using matplotlib.
Note: Here bird image is used with dimension 32 x 32 x 3, crop images 5x5x3 with stride =1.
image = Image.open('bird.png')
tensreal = trans(image)
trans = transforms.Compose([transforms.Resize(32),
transforms.ToTensor(),
])
stride = 1
crop_height = 5
crop_width = 5
img_height = 32
img_width = 32
tens_list = []
for i in range(0,img_width-crop_width,stride):
for j in range(0,img_height-crop_height ,stride):
tens = trans(image)
tens1 = tens[:, j:j+crop_height, i:i+crop_width]
tens_list.append(tens1)
all_tens = torch.stack(tens_list)
print(all_tens.size())

Resources