OpenCV Rotation with warpAffine in Python Causes Erroneous Border - python-3.x

Check out this Python code:
degrees = 90
center = (24, 24)
img = np.ones((48,48,3)) * 255
mat = cv2.getRotationMatrix2D(center, degrees, 1.0)
img = cv2.warpAffine(img, mat, (48, 48))
My expectation is that a 3 channel, fully saturated, white square will be created and stored in img. After which, it'll be rotated by 90 degrees. Rotating a white square by 90 degrees should result in ... an indistinguishable white square. But when I:
plt.imshow(img)
plt.show(img)
I see an erroneous black border:
Is there any way to get warpAffine working as expected, i.e. rotate the image without an erroneous border? I've tried the following modifications to no avail:
center = (23, 23)
center = (24, 23)
center = (23, 24)
center = (25, 25)
center = (24, 25)
center = (25, 24)

You should be using the exact center of the image rather than the next closest thing. The rotation is slightly off center using (24,24).
Since getRotationMatrix2D accepts a Point2f, you should be passing the center as (23.5,23.5), as it is the midway point between 0 and 47.

Related

How do I find corners of a paper when there are printed corners/lines on paper itself?

I'm using openCV in Python to find the corners of a sheet of paper to unwarp it.
img = cv2.imread(images[i])
corners = cv2.goodFeaturesToTrack(cv2.cvtColor(img,cv2.COLOR_BGR2GRAY),4,.01,1000,useHarrisDetector=True,k=.04)
corners = np.float32(corners)
print(corners)
ratio = 1.6
cardH = math.sqrt((corners[2][0][0] - corners[1][0][0]) * (corners[2][0][0] - corners[1][0][0]) + (corners[2][0][1] - corners[1][0][1]) * (
corners[2][0][1] - corners[1][0][1]))
cardW = ratio * cardH;
pts2 = np.float32(
[[corners[0][0][0], corners[0][0][1]], [corners[0][0][0] + cardW, corners[0][0][1]], [corners[0][0][0] + cardW, corners[0][0][1] + cardH],
[corners[0][0][0], corners[0][0][1] + cardH]])
M = cv2.getPerspectiveTransform(corners, pts2)
offsetSize = 500
transformed = np.zeros((int(cardW + offsetSize), int(cardH + offsetSize)), dtype=np.uint8);
dst = cv2.warpPerspective(img, M, transformed.shape)
Before:
https://imgur.com/a/H7HjFro
After:
https://imgur.com/a/OA6Iscq
As you can see with these images, they're detecting edges inside the paper itself, rather than the corner of the paper. Should I consider using a different algorithm entirely? I'm quite lost.
I've tried increasing the minimum euclidean distance to 1000, but that really didn't do anything.
Please note, this no one's real information, this is a fake dataset found on Kaggle.
The kaggle dataset can be found https://www.kaggle.com/mcvishnu1/fake-w2-us-tax-form-dataset
Here is one way to do that in Python/OpenCV.
Note that the found corners are listed counter-clockwise from the top-most corner.
Read the input
Convert to gray
Gaussian blur
Otsu threshold
Morphology open/close to clean up the threshold
Get largest contour
Approximate a polygon from the contour
Get the corners
Draw the polygon on the input
Compute side lengths
Compute output corresponding corners
Get perspective transformation matrix from corresponding corner points
Warp the input image according to the matrix
Save the results
Input:
import cv2
import numpy as np
# read image
img = cv2.imread("efile.jpg")
# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# blur image
blur = cv2.GaussianBlur(gray, (3,3), 0)
# do otsu threshold on gray image
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# apply morphology
kernel = np.ones((7,7), np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)
# get largest contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
area_thresh = 0
for c in contours:
area = cv2.contourArea(c)
if area > area_thresh:
area_thresh = area
big_contour = c
# draw white filled largest contour on black just as a check to see it got the correct region
page = np.zeros_like(img)
cv2.drawContours(page, [big_contour], 0, (255,255,255), -1)
# get perimeter and approximate a polygon
peri = cv2.arcLength(big_contour, True)
corners = cv2.approxPolyDP(big_contour, 0.04 * peri, True)
# draw polygon on input image from detected corners
polygon = img.copy()
cv2.polylines(polygon, [corners], True, (0,0,255), 1, cv2.LINE_AA)
# Alternate: cv2.drawContours(page,[corners],0,(0,0,255),1)
# print the number of found corners and the corner coordinates
# They seem to be listed counter-clockwise from the top most corner
print(len(corners))
print(corners)
# for simplicity get average of top/bottom side widths and average of left/right side heights
# note: probably better to get average of horizontal lengths and of vertical lengths
width = 0.5*( (corners[0][0][0] - corners[1][0][0]) + (corners[3][0][0] - corners[2][0][0]) )
height = 0.5*( (corners[2][0][1] - corners[1][0][1]) + (corners[3][0][1] - corners[0][0][1]) )
width = np.int0(width)
height = np.int0(height)
# reformat input corners to x,y list
icorners = []
for corner in corners:
pt = [ corner[0][0],corner[0][1] ]
icorners.append(pt)
icorners = np.float32(icorners)
# get corresponding output corners from width and height
ocorners = [ [width,0], [0,0], [0,height], [width,height] ]
ocorners = np.float32(ocorners)
# get perspective tranformation matrix
M = cv2.getPerspectiveTransform(icorners, ocorners)
# do perspective
warped = cv2.warpPerspective(img, M, (width, height))
# write results
cv2.imwrite("efile_thresh.jpg", thresh)
cv2.imwrite("efile_morph.jpg", morph)
cv2.imwrite("efile_polygon.jpg", polygon)
cv2.imwrite("efile_warped.jpg", warped)
# display it
cv2.imshow("efile_thresh", thresh)
cv2.imshow("efile_morph", morph)
cv2.imshow("efile_page", page)
cv2.imshow("efile_polygon", polygon)
cv2.imshow("efile_warped", warped)
cv2.waitKey(0)
Thresholded image:
Morphology cleaned image:
Polygon drawn on input:
Extracted Corners (counterclockwise from top right corner)
4
[[[693 67]]
[[ 23 85]]
[[ 62 924]]
[[698 918]]]
Warped Result:

Measuring the size of bubbles from a picture of material particles - Python-OPenCV

I am trying to measure the size and count of bubbles available in image taken from real world material particles.
I have tried the approach present Here (second one with opencv). But some how it doesnt work probably because of the bubbles, because image is kind of an average quality.
Can some one please direct me to correct direction or provide recommendation?
The reference image, i am trying process is
Result:
It doesn't detects the circles, in fact at finding contours step, it gets a zero therefore the test is not passed.
Made some changes. Able to detect some bubbles but still not good. Detected bubble image is below
Thanks
Code:
import cv2
image = cv2.imread('....')
# Gray, blur, adaptive threshold
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (11,5), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
# Morphological transformations
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# Find contours
cnts = cv2.findContours(opening, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
# Find perimeter of contour
perimeter = cv2.arcLength(c, True)
# Perform contour approximation
approx = cv2.approxPolyDP(c, 0.04 * perimeter, True)
if len(approx) > 6:
# Obtain bounding rectangle to get measurements
x,y,w,h = cv2.boundingRect(c)
# Find measurements
diameter = w
radius = w/2
# Find centroid
M = cv2.moments(c)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# Draw the contour and center of the shape on the image
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),4)
cv2.drawContours(image,[c], 0, (36,255,12), 4)
cv2.circle(image, (cX, cY), 15, (320, 159, 22), -1)
# Draw line and diameter information
cv2.line(image, (x, y + int(h/2)), (x + w, y + int(h/2)), (156, 188, 24), 3)
cv2.putText(image, "Diameter: {}".format(diameter), (cX - 50, cY - 50), cv2.FONT_HERSHEY_SIMPLEX, 3, (156, 188, 24), 3)
cv2.imwrite('...', image)
cv2.imwrite('...', thresh)
cv2.imwrite('...', opening)

Image segmentation of objects in any illumination(low or high)

The problem I have at hand is to draw boundaries around a white ball. But the ball is present in different illuminations. Using canny edge detections and Hough transform for circles, I am able to detect the ball in bright light/partial bright light but not in low illumination.
So can anyone help with this problem.
The code that I have tried is below.
img=cv2.imread('14_04_2018_10_38_51_.8242_P_B_142_17197493.png.png')
cimg=img.copy()
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.medianBlur(img,5)
edges=cv2.Canny(edges,200,200)
circles = cv2.HoughCircles(edges,cv2.HOUGH_GRADIENT,1,20,
param1=25,param2=10,minRadius=0,maxRadius=0)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(255,255,255),2)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv2.imwrite('segmented_out.png',cimg)
else:
print("no circles")
cv2.imwrite('edges_out.png',edges)
In the image below we need to segment if the ball is in the shadow region as well.
The output should be something like below images..
Well I am not very experienced in OpenCV or Python but I am learning as well. Probably not very pythonic piece of code but you could try this:
import cv2
import math
circ=0
n = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220]
img = cv2.imread("ball1.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
for i in n:
ret, threshold = cv2.threshold(gray,i,255,cv2.THRESH_BINARY)
im, contours, hierarchy = cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
for j in range(0, len(contours)):
size = cv2.contourArea(contours[j])
if 500 < size < 5000:
if circ > 0:
(x,y),radius = cv2.minEnclosingCircle(contours[j])
radius = int(radius)
area = cv2.contourArea(contours[j])
circif = 4*area/(math.pi*(radius*2)**2)
if circif > circ:
circ = float(circif)
radiusx = radius
center = (int(x),int(y))
elif circ == 0:
(x,y),radius = cv2.minEnclosingCircle(contours[j])
radius = int(radius)
area = cv2.contourArea(contours[j])
circ = 4*area/(math.pi*(radius*2)**2)
else:
pass
cv2.circle(img,center,radiusx,(0,255,0),2)
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.detroyAllWindows()
What it does is acctually you convert your picture to grayscale and apply different threshold settings to it. Then you eliminate noises with adding size to your specific contour. When you find it, you check its circularity (NOTE: it is not a scientific formula) and compare it to the next circularity. Perfect circle should return the result 1, so the highest number that will get in a contour (of all the contours) will be your ball.
Result:
NOTE: I haven't tried increasing the limit of size so maybe higher limit could return better result if you have a high resolution picture
Working with grayscale image will make you subject to different light conditions.
To be free from this I suggest to work in HSV color space, then use the Hue component instead of the grayscale image.
Hue is independent from the light condition, since it gives you information about the color, regardless of its Saturation or Value (a value bound to the brightness of the image).
This might bring you some clarity about color spaces and which is best to use for image segmentation.
In your case here. We have a white ball.White is not a color by itself.The main factor here is, what kind light actually falls on the white ballAs the kind of light that falls on it has a direct influence on the kind of extraction you might plan to do using a color space like HSV as mentioned above by #magicleon
HSV is your best bet for segmentation here.Using
whiteObject = cv2.inRange(hsvImage,lowerHSVLimit,upperHSVLimit)
lowerHSVLimit and upperHSVLimit HSV color range
Keeping in mind that the conditions
1) The image have similar conditions while they were clicked
2) You cover all the ranges of HSV before extraction
Hope you get an idea
Consider this example
Selecting a particular hue range from 45 to 60
Code
image = cv2.imread('allcolors.png')
hsvImg = cv2.cvtColor(image,cv2.COLOR_BGR2HSV)
lowerHSVLimit = np.array([45,0,0])
upperHSVLimit = np.array([60,255,255])
colour = cv2.inRange(hsvImg,lowerHSVLimit,upperHSVLimit)
plt.subplot(111), plt.imshow(colour,cmap="gray")
plt.title('hue range from 45 to 60'), plt.xticks([]), plt.yticks([])
plt.show()
Here the hue selected from 45 to 60

Convert own image to MNIST's image

I am newbie of tensorflow.
I trained the digit prediction model using MNIST's train data.
And then I test the model using my own image.
It cannot predict the actual result.
The problems are :
MNIST's images are needed black and white
The images are size normalized to fit in a 20x20 pixel box and there are centered in a 28x28 image using the center of mass.
I don't want to use OpenCV
The question is How to shift my own handwritten digit image to the center of 28x28 image. Own image can be any color and that image to change Black and White MNIST's image
from PIL import Image, ImageFilter
def imageprepare(argv):
"""
This function returns the pixel values.
The imput is a png file location.
"""
im = Image.open(argv).convert('L')
width = float(im.size[0])
height = float(im.size[1])
newImage = Image.new('L', (28, 28), (255)) # creates white canvas of 28x28 pixels
if width > height: # check which dimension is bigger
# Width is bigger. Width becomes 20 pixels.
nheight = int(round((20.0 / width * height), 0)) # resize height according to ratio width
if (nheight == 0): # rare case but minimum is 1 pixel
nheight = 1
# resize and sharpen
img = im.resize((20, nheight), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wtop = int(round(((28 - nheight) / 2), 0)) # calculate horizontal position
newImage.paste(img, (4, wtop)) # paste resized image on white canvas
else:
# Height is bigger. Heigth becomes 20 pixels.
nwidth = int(round((20.0 / height * width), 0)) # resize width according to ratio height
if (nwidth == 0): # rare case but minimum is 1 pixel
nwidth = 1
# resize and sharpen
img = im.resize((nwidth, 20), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wleft = int(round(((28 - nwidth) / 2), 0)) # caculate vertical pozition
newImage.paste(img, (wleft, 4)) # paste resized image on white canvas
# newImage.save("sample.png
tv = list(newImage.getdata()) # get pixel values
# normalize pixels to 0 and 1. 0 is pure white, 1 is pure black.
tva = [(255 - x) * 1.0 / 255.0 for x in tv]
print(tva)
return tva
x=imageprepare('./image.png')#file path here
print(len(x))# mnist IMAGES are 28x28=784 pixels
I would use numpy recipe like this one --
https://www.kaggle.com/c/digit-recognizer/forums/t/6366/normalization-and-centering-of-images-in-mnist
You could probably remap this to pure TensorFlow pipeline, but I'm not sure it's necessary given that it's tiny images.
Also you would get better accuracy if you went the other way -- instead of normalizing your input data, make your network robust to lack of normalization by training on a larger dataset of randomly shifted/rescaled MNIST digits.

Adjusting set_line_width() to correct ratio?

I'm using Cairo to draw figures. I found that Cairo uses a "absolute coordinate" when drawing. It is a flexible and comfortable way, except specify the line_width. Because of the ratio of the below image is not 1:1, when the "absolute coordinate" converted to "real coordinate", the width of the lines are not same.
WIDTH = 960
HEIGHT = 640
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
ctx = cairo.Context(surface)
ctx.scale(WIDTH, HEIGHT)
ctx.rectangle(0, 0, 1, 1)
ctx.set_source_rgb(255, 255, 255)
ctx.fill()
ctx.set_source_rgb(0, 0, 0)
ctx.move_to(0.5, 0)
ctx.line_to(0.5, 1)
ctx.move_to(0, 0.5)
ctx.line_to(1, 0.5)
ctx.set_line_width(0.01)
ctx.stroke()
What is the correct way to make line_width shown as the same ratio in the output image?
Undo your call to ctx.scale() before calling stroke(), for example via:
ctx.save()
ctx.set_line_width(2)
ctx.identity_matrix()
ctx.restore()
(The save()/restore() pair applies all your transformations again afterwards)

Resources