Foggy images detection [closed] - python

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
experts
I am doing a project about analysing the green trends of the trees.
In term of initial processing, I've got intensity value for the whole 500 images and decided the threshold for selecting good images from the datasets. It's around from 60 to 122, and I named the range as good images.
However, the tricky issue was that some of the foggy images were also in that range. The intensity of good images is from 90 to 120, some of the foggy images have the same intensity value compared to the good one.
the intensity of iamges
So far, it's the code for getting the intensity of one image and the average of the intensity for 500 images
im = Image.open('IMAG0087.JPG')
im_grey = im.convert('LA')
width,height = im.size
total=0
for i in range(0,width):
for j in range(0,height):
total += im.getpixel((i,j))[0]
mean = total / (width * height)
print mean
Getting the average of the intensity for 500 images:
results = []
for dirpath, dirnames, filenames in os.walk("/Users/Terrynightbleach/Desktop/Dataset-Tree/No2_Fraser_Gully/"):
for filename in [f for f in filenames if f.endswith('.JPG')]:
img = cv2.imread(filename)
avg_color_per_row = numpy.average(img, axis=0)
avg_color = numpy.average(avg_color_per_row, axis=0)
results.append(sum(avg_color/3))
np_results = np.array(results)
plt.hist(np_results,bins=100)
plt.show()
Are there any other values that I am supposed to use in order to discard those foggy images out from the datasets? And how should I achieve it by python? That would be really helpful if you could show the code.
Thank you so much!!!!!

As you can see your foggy images have very low horizontal variance. Therefore you could compute average horizontal variance for each image. Here's a quick example:
import Image
import glob
def slow_horizontal_variance(im):
'''Return average variance of horizontal lines of a grayscale image'''
width, height = im.size
if not width or not height: return 0
vars = []
pix = im.load()
for y in range(height):
row = [pix[x,y] for x in range(width)]
mean = sum(row)/width
variance = sum([(x-mean)**2 for x in row])/width
vars.append(variance)
return sum(vars)/height
for fn in glob.glob('*.png'):
im = Image.open(fn).convert('L')
var = slow_horizontal_variance(im)
fog = var < 200 # FOG THRESHOLD
print ('%5.0f - %5s - %s' % (var, fog and 'FOGGY' or 'SHARP', fn))
Output:
104 - FOGGY - 00.png
298 - SHARP - 01.png
597 - SHARP - 02.png
130 - FOGGY - 03.png
The images:
00.png
01.png
02.png
03.png
So using numpy, instead of your code:
avg_color_per_row = numpy.average(img, axis=0)
avg_color = numpy.average(avg_color_per_row, axis=0)
You would have something like:
intensity_variance_per_row = numpy.var(img, axis=0)
avg_variance = numpy.average(intensity_variance_per_row, axis=0)
fog = avg_variance < 200 # FOG THRESHOLD
print ('%5.0f - %5s - %s' % (avg_variance, fog and 'FOGGY' or 'SHARP', filename))
(Unfortunately, I'm too lazy to install numpy on this machine, so I cannot confirm it works as well as my slow code above. If it doesn't, you might need to change numpy.var(img, axis=0) to numpy.var(img, axis=1).)

Related

opencv align two images by keypoints [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
The community reviewed whether to reopen this question 9 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I have two images
image1(dog): Download link
image2 (bird):
I want to align image2 on image1 by keypoints. So the goal that both keypoints overlaps and the bird image is resized and scaled on top of the dog image.
Image 1 with keypoints:
Image two with keypoints:
This are my keypoints (csv file). The coordinates are x and y in the original image (download links at top):
bird_x,bird_y,dog_x,dog_y
0,43,265,48
88,12,297,29
172,1,332,23
211,17,349,32
283,60,378,60
143,109,321,98
How can I do this with opencv and python?
A quick rough way to do it is by resizing the smaller image to the bounding box formed by the keypoints of the bigger image.
import cv2
import numpy as np
img_dog = cv2.imread("akita.jpg")
img_bird = cv2.imread("gull.png")
#Reconstruct the keypoints
bird_keypoints = ((0,43), (88,12), (172,1), (211,17), (283,60), (143,109))
dog_keypoints = ((265,48), (297,29), (332,23), (349,32), (378,60), (321, 98))
#New dimension of the smaller image - calculate keypoints max x and y range
min_x = min([p[0] for p in dog_keypoints])
max_x = max([p[0] for p in dog_keypoints])
min_y = min([p[1] for p in dog_keypoints])
max_y = max([p[1] for p in dog_keypoints])
scale_height = max_y - min_y
scale_width = max_x - min_x
center = (0.5*(min_x + max_x), 0.5*(min_y + max_y))
#resize smaller image
img_bird_scaled = cv2.resize(img_bird, (scale_width,scale_height),
interpolation=cv2.INTER_AREA)
#overwrite part of the dog image with the new scaled smaller image
img_dog[min_y:max_y, min_x:max_x] = img_bird_scaled
cv2.imshow("test", img_dog)
If you need to paste only the bird without the background, you will have to deal with the alpha channel.

How to split an image horizontally into equal-sized pieces?

I am trying to split this input image into three/four equal-sized pieces horizontally but I am not getting the desired output. I don't know what I am doing wrong.
Here is the code which I wrote to split it:
import os.path
import numpy as np
from PIL import image
input_1 = "/home/task-1/split_operation/111.jpg"
outputPath = "/home/task-1/split_operation/"
im = Image.open(input_1)
x_width, y_height = im.size
split = np.int(x_width / 3)
outputFileFormat = "{0}-{1}.jpg"
baseName = "cropped_1"
for i in range(0, x_width, split):
x = split + i
box = (x, 0, x + split, y_height)
a = im.crop(box)
a.load()
outputName = os.path.join(outputPath, outputFileFormat.format(baseName, i + 1))
a.save(outputName, "JPEG")
The input image:
The output images I currently get:
As you can see last two images are black. I don't know why I am getting a black image.
Your problem is that when i=0 your first x is split which means you skip the first image. You can actually see that the first image doesn't align with the edge of the original. In addition, you are rounding the pixels to use as a step to the range function but this creates a problematic behaviour: your image's width is 800 pixels. This means that your split is 266. This means that the last i value is going to be 798 and this is why the third image has a thin line of 2 pixels at the left edge.
A better way will be to generate all "edges" using np.linspace. This assures the right amount of pictures in the exact right sizes instead of bothering with range loops and width calculations. Just create the edges list and use it in the loop as follows:
...
pictures = 3
edges = np.linspace(0, x_width, pictures+1)
for start, end in zip(edges[:-1], edges[1:]):
box = (start, 0, end, y_height)
...
The rest stays the same.
Running this with pictures = 3 will produce:
Running with pictures = 4 will produce:
`
I will assume that width of the image is 900px.
Then split = 300
Let's look at first few iterations of your for loop:
i = 0; x = 300; x + split = 600
i = 300; x = 300 + 300 = 600; x + split = 900
...
As you can see, part of the image (0--300px) is missing.
It could be easily fixed:
for i in range(0, x_width, split):
box = (i, 0, i + split, y_height)
...
The reason you getting black images is just because you try to access pixels that are not part of the image.

How to delete or clear contours from image?

I'm working with license plates, what I do is apply a series of filters to it, such as:
Grayscale
Blur
Threshhold
Binary
The problem is when I doing this, there are some contour like this image at borders, how can I clear them? or make it just black color (masked)? I used this code but sometimes it falls.
# invert image and detect contours
inverted = cv2.bitwise_not(image_binary_and_dilated)
contours, hierarchy = cv2.findContours(inverted,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# get the biggest contour
biggest_index = -1
biggest_area = -1
i = 0
for c in contours:
area = cv2.contourArea(c)
if area > biggest_area:
biggest_area = area
biggest_index = i
i = i+1
print("biggest area: " + str(biggest_area) + " index: " + str(biggest_index))
cv2.drawContours(image_binary_and_dilated, contours, biggest_index, [0,0,255])
center, size, angle = cv2.minAreaRect(contours[biggest_index])
rot_mat = cv2.getRotationMatrix2D(center, angle, 1.)
#cv2.warpPerspective()
print(size)
dst = cv2.warpAffine(inverted, rot_mat, (int(size[0]), int(size[1])))
mask = dst * 0
x1 = max([int(center[0] - size[0] / 2)+1, 0])
y1 = max([int(center[1] - size[1] / 2)+1, 0])
x2 = int(center[0] + size[0] / 2)-1
y2 = int(center[1] + size[1] / 2)-1
point1 = (x1, y1)
point2 = (x2, y2)
print(point1)
print(point2)
cv2.rectangle(dst, point1, point2, [0,0,0])
cv2.rectangle(mask, point1, point2, [255,255,255], cv2.FILLED)
masked = cv2.bitwise_and(dst, mask)
#cv2_imshow(imgg)
cv2_imshow(dst)
cv2_imshow(masked)
#cv2_imshow(mask)
Some results:
The original plates were:
Good result 1
Good result 2
Good result 3
Good result 4
Bad result 1
Bad result 2
Binary plates are:
Image 1
Image 2
Image 3
Image 4
Image 5 - Bad result 1
Image 6 - Bad result 2
How can I fix this code? only that I want to avoid that bad result or improve it.
INTRODUCTION
What you are asking starts to become complicated, and I believe there is not anymore a right or wrong answer, just different ways to do this. Almost all of them will yield positive and negative results, most likely in a different ratio. Having a 100% positive result is quite a challenging task, and I do believe my answer does not reach it. Yet it can be the basis for a more sophisticated work towards that goal.
MY PROPOSAL
So, I want to make a different proposal here.
I am not 100% sure why you are doing all the steps, and I believe some of them could be unnecessary.
Let's start from the problem: you want to remove the white parts on the borders (which are not numbers).
So, we need an idea about how to distinguish them from the letters, in order to correctly tackle them.
If we just try to contour and warp, it is likely to work on some images and not on others, because not all of them look the same. This is the hardest problem to have a general solution that works for many images.
What are the difference between the characteristics of the numbers and the characteristics of the borders (and other small points?):
after thinking about that, I would say: the shapes! That meaning, if you would imagine a bounding box around a letter/number, it would look like a rectangle, whose size is related to the image size. While in the case of the border, they are usually very large and narrow, or too small to be considered a letter/number (random points).
Therefore, my guess would be on segmentation, dividing the features via their shape. So we take the binary image, we remove some parts using the projection on their axes (as you correctly asked in the previous question and I believe we should use) and we get an image where each letter is separated from the white borders.
Then we can segment and check the shape of each segmented object, and if we think these are letters, we keep them, otherwise we discard them.
THE CODE
I wrote the code before as an example on your data. Some of the parameters are tuned on this set of images, so they may have to be relaxed for a larger dataset.
import cv2
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
import scipy.ndimage as ndimage
# do this for all the images
num_images = 6
plt.figure(figsize=(16,16))
for k in range(num_images):
# read the image
binary_image = cv2.imread("binary_image/img{}.png".format(k), cv2.IMREAD_GRAYSCALE)
# just for visualization purposes, I create another image with the same shape, to show what I am doing
new_intermediate_image = np.zeros((binary_image.shape), np.uint8)
new_intermediate_image += binary_image
# here we will copy only the cleaned parts
new_cleaned_image = np.zeros((binary_image.shape), np.uint8)
### THIS CODE COMES FROM THE PREVIOUS ANSWER:
# https://stackoverflow.com/questions/62127537/how-to-clean-binary-image-using-horizontal-projection?noredirect=1&lq=1
(rows,cols)=binary_image.shape
h_projection = np.array([ x/rows for x in binary_image.sum(axis=0)])
threshold_h = (np.max(h_projection) - np.min(h_projection)) / 10
print("we will use threshold {} for horizontal".format(threshold))
# select the black areas
black_areas_horizontal = np.where(h_projection < threshold_h)
for j in black_areas_horizontal:
new_intermediate_image[:, j] = 0
v_projection = np.array([ x/cols for x in binary_image.sum(axis=1)])
threshold_v = (np.max(v_projection) - np.min(v_projection)) / 10
print("we will use threshold {} for vertical".format(threshold_v))
black_areas_vertical = np.where(v_projection < threshold_v)
for j in black_areas_vertical:
new_intermediate_image[j, :] = 0
### UNTIL HERE
# define the features we are looking for
# this parameters can also be tuned
min_width = binary_image.shape[1] / 14
max_width = binary_image.shape[1] / 2
min_height = binary_image.shape[0] / 5
max_height = binary_image.shape[0]
print("we look for feature with width in [{},{}] and height in [{},{}]".format(min_width, max_width, min_height, max_height))
# segment the iamge
labeled_array, num_features = ndimage.label(new_intermediate_image)
# loop over all features found
for i in range(num_features):
# get a bounding box around them
slice_x, slice_y = ndimage.find_objects(labeled_array==i)[0]
roi = labeled_array[slice_x, slice_y]
# check the shape, if the bounding box is what we expect, copy it to the new image
if roi.shape[0] > min_height and \
roi.shape[0] < max_height and \
roi.shape[1] > min_width and \
roi.shape[1] < max_width:
new_cleaned_image += (labeled_array == i)
# print all images on a grid
plt.subplot(num_images,3,1+(k*3))
plt.imshow(binary_image)
plt.subplot(num_images,3,2+(k*3))
plt.imshow(new_intermediate_image)
plt.subplot(num_images,3,3+(k*3))
plt.imshow(new_cleaned_image)
that produces the output (in the grid, left image are the input images, central one are the images after the mask based on histogram projections, and on the right are the cleaned images):
CONCLUSIONS:
As said above, this method does not yield 100% positive results. The last picture has lower quality and some parts are unconnected, and they are lost in the process. I personally believe this is a price to pay to get cleaner image, and if you have a lot of images, it won't be a problem, and you can remove those kind of images. Overall, I think this method returns quite clear images, where all other parts that are not letters or numbers are correctly removed.
ADVANTAGES
the image is clean, nothing more than letters or numbers are kept
the parameters can be tuned, and should be consistent across images
in case of problem, using some prints or some debugging on the loop that chooses the features to keep should make it easier to understand where are the problem and correct them
LIMITATIONS
it may fail in some cases where letters and numbers touch the white borders, which seems quite possible. It is handled from the black_areas created using the projection, but I am not so confident this will work 100% of the time.
some small parts of the numbers can be lost during the process, as in the last picture.

OpenCV: Find similar object with contours matching

I have a bundle of different images with birds but some of them contains feathers, slightly visible birds etc. I need to find images where the bird visible well and remove images with feathers, far distant birds, etc.
I already tried ORB, simple template matching, Canny edge detection. And I cannot use neural nets.
Now i try with such algorithm:
Binarize template image to get shapes
Slide window over another binarized image with sliding window and calculate matchShape with template in every window
Find best match
As you can see this method gives me strange result
Binary template
.
Shape on the other binary image, for example:
I calculated matchShapes in different parts of this image and the best result ~ 0.05 I got in this part:
which is obviously not similar to original shape.
Code for sliding window:
import cv2
OFFSET = 5
SCALE_RATIO = [0.5, 1]
def get_scaled_list(img_path, template):
matcher_list = []
img = cv2.imread(img_path)
#JUST BINARIZATION AND RESIZING
img = preprocess(resize_image(img))
height, width = img.shape
# building size of scale window
for scaler in SCALE_RATIO:
x_point = 0
y_point = 0
x1_point = int(width * scaler)
y1_point = x1_point
if x1_point > height:
y1_point = height
while y1_point <= height:
while x1_point <= width:
img1 = img[y_point:y1_point, x_point:x1_point]
#Comparing template and part of image
diff = cv2.matchShapes(template, img1, cv2.CONTOURS_MATCH_I1, 0)
data_tuple = (img_path, x_point, y_point, int(width * scaler), diff)
matcher_list.append(data_tuple)
x_point += OFFSET
x1_point += OFFSET
x_point = 0
x1_point = int(width * scaler)
y_point += OFFSET
y1_point += OFFSET
return matcher_list
How can I perform correct shape matching and why is the best result performs here?
The naive window sliding method with a rigid template will work very poorly. In particular, the sizes are different making correct overlap impossible.
What you are trying to achieve is difficult because you only have edge information and the edges are complex, broken in several independent arcs and with junctions.
You can find many solutions when you have a single closed curve (lookup "elastic contour matching" for instance), but not for your case. This would be a case of "approximate elastic graph matching".
Other possible approaches are by special distance functions such as the chamfer or Hausdorff distances, but you can still be stuck because of the size mismatch.

Intensity normalization of image using Python+PIL - Speed issues

I'm working on a little problem in my sparetime involving analysis of some images obtained through a microscope. It is a wafer with some stuff here and there, and ultimately I want to make a program to detect when certain materials show up.
Anyways, first step is to normalize the intensity across the image, since the lens does not give uniform lightning. Currently I use an image, with no stuff on, only the substrate, as a background, or reference, image. I find the maximum of the three (intensity) values for RGB.
from PIL import Image
from PIL import ImageDraw
rmax = 0;gmax = 0;bmax = 0;rmin = 300;gmin = 300;bmin = 300
im_old = Image.open("test_image.png")
im_back = Image.open("background.png")
maxx = im_old.size[0] #Import the size of the image
maxy = im_old.size[1]
im_new = Image.new("RGB", (maxx,maxy))
pixback = im_back.load()
for x in range(maxx):
for y in range(maxy):
if pixback[x,y][0] > rmax:
rmax = pixback[x,y][0]
if pixback[x,y][1] > gmax:
gmax = pixback[x,y][1]
if pixback[x,y][2] > bmax:
bmax = pixback[x,y][2]
pixnew = im_new.load()
pixold = im_old.load()
for x in range(maxx):
for y in range(maxy):
r = float(pixold[x,y][0]) / ( float(pixback[x,y][0])*rmax )
g = float(pixold[x,y][1]) / ( float(pixback[x,y][1])*gmax )
b = float(pixold[x,y][2]) / ( float(pixback[x,y][2])*bmax )
pixnew[x,y] = (r,g,b)
The first part of the code determines the maximum intensity of the RED, GREEN and BLUE channels, pixel by pixel, of the background image, but needs only be done once.
The second part takes the "real" image (with stuff on it), and normalizes the RED, GREEN and BLUE channels, pixel by pixel, according to the background. This takes some time, 5-10 seconds for an 1280x960 image, which is way too slow if I need to do this to several images.
What can I do to improve the speed? I thought of moving all the images to numpy arrays, but I can't seem to find a fast way to do that for RGB images.
I'd rather not move away from python, since my C++ is quite low-level, and getting a working FORTRAN code would probably take longer than I could ever save in terms of speed :P
import numpy as np
from PIL import Image
def normalize(arr):
"""
Linear normalization
http://en.wikipedia.org/wiki/Normalization_%28image_processing%29
"""
arr = arr.astype('float')
# Do not touch the alpha channel
for i in range(3):
minval = arr[...,i].min()
maxval = arr[...,i].max()
if minval != maxval:
arr[...,i] -= minval
arr[...,i] *= (255.0/(maxval-minval))
return arr
def demo_normalize():
img = Image.open(FILENAME).convert('RGBA')
arr = np.array(img)
new_img = Image.fromarray(normalize(arr).astype('uint8'),'RGBA')
new_img.save('/tmp/normalized.png')
See http://docs.scipy.org/doc/scipy/reference/generated/scipy.misc.fromimage.html#scipy.misc.fromimage
You can say
databack = scipy.misc.fromimage(pixback)
rmax = numpy.max(databack[:,:,0])
gmax = numpy.max(databack[:,:,1])
bmax = numpy.max(databack[:,:,2])
which should be much faster than looping over all (r,g,b) triplets of your image.
Then you can do
dataold = scip.misc.fromimage(pixold)
r = dataold[:,:,0] / (pixback[:,:,0] * rmax )
g = dataold[:,:,1] / (pixback[:,:,1] * gmax )
b = dataold[:,:,2] / (pixback[:,:,2] * bmax )
datanew = numpy.array((r,g,b))
imnew = scipy.misc.toimage(datanew)
The code is not tested, but should work somehow with minor modifications.
This is partially from FolksTalk webpage:
from PIL import Image
import numpy as np
# Read image file
in_file = "my_image.png"
# convert('RGB') for PNG file type
image = Image.open(in_file).convert('RGB')
pixels = np.asarray(image)
# Convert from integers to floats
pixels = pixels.astype('float32')
# Normalize to the range 0-1
pixels /= 255.0

Categories

Resources