Signature extraction from a JPEG File Code Setup - python

I am trying to extract a signature from a document using some code that I found on GIT.
The error is as follows: "ValueError: Image RGB array must be uint8 or floating point; found int32"
No matter what image I use, I get the error. I am a novice at image extraction so any help would be appreciated.
The code used:
def extract_signature(source_image):
"""Extract signature from an input image.
Parameters
----------
source_image : numpy ndarray
The pinut image.
Returns
-------
numpy ndarray
An image with the extracted signatures.
"""
# read the input image
img = source_image
img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1] # ensure binary
# connected component analysis by scikit-learn framework
blobs = img > img.mean()
blobs_labels = measure.label(blobs, background=1)
# image_label_overlay = label2rgb(blobs_labels, image=img)
fig, ax = plt.subplots(figsize=(10, 6))
'''
# plot the connected components (for debugging)
ax.imshow(image_label_overlay)
ax.set_axis_off()
plt.tight_layout()
plt.show()
'''
the_biggest_component = 0
total_area = 0
counter = 0
average = 0.0
for region in regionprops(blobs_labels):
if (region.area > 10):
total_area = total_area + region.area
counter = counter + 1
# print region.area # (for debugging)
# take regions with large enough areas
if (region.area >= 250):
if (region.area > the_biggest_component):
the_biggest_component = region.area
average = (total_area/counter)
print("the_biggest_component: " + str(the_biggest_component))
print("average: " + str(average))
# experimental-based ratio calculation, modify it for your cases
# a4_small_size_outliar_constant is used as a threshold value to remove connected outliar connected pixels
# are smaller than a4_small_size_outliar_constant for A4 size scanned documents
a4_small_size_outliar_constant = ((average/constant_parameter_1)*constant_parameter_2)+constant_parameter_3
print("a4_small_size_outliar_constant: " + str(a4_small_size_outliar_constant))
# experimental-based ratio calculation, modify it for your cases
# a4_big_size_outliar_constant is used as a threshold value to remove outliar connected pixels
# are bigger than a4_big_size_outliar_constant for A4 size scanned documents
a4_big_size_outliar_constant = a4_small_size_outliar_constant*constant_parameter_4
print("a4_big_size_outliar_constant: " + str(a4_big_size_outliar_constant))
# remove the connected pixels are smaller than a4_small_size_outliar_constant
pre_version = morphology.remove_small_objects(blobs_labels, a4_small_size_outliar_constant)
# remove the connected pixels are bigger than threshold a4_big_size_outliar_constant
# to get rid of undesired connected pixels such as table headers and etc.
component_sizes = np.bincount(pre_version.ravel())
too_small = component_sizes > (a4_big_size_outliar_constant)
too_small_mask = too_small[pre_version]
pre_version[too_small_mask] = 0
# save the the pre-version which is the image is labelled with colors
# as considering connected components
plt.imsave('pre_version.png', pre_version)
# read the pre-version
img = cv2.imread('pre_version.png', 0)
# ensure binary
img = cv2.threshold(img, 0, 255,
cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
# save the the result
# cv2.imwrite("output.png", img)
return img
If anyone has any suggestions on tips on what to change or improve, that would be greatly appreciated.

Related

How should I adjust my parameters to be able to detect certain white blobs on top of a black backgroung

I have a very simple task which is detecting some very obvious blobs. I am very new to image processing, I am probably making a very simple mistake. I prepared a blob detection function by using this example and I am using this function on an artificial image. My original task is to detect blob in a much complex image but I thought I can start small. Originally I have a gray image with small whitish(less gray) dirts over it. Anyways my problem is I cannot detect any blobs. I think I am failing on adjusting the parameters. Although I tried many different combinations, I couldn't get any result. Here is my simpler artificial image:
Here is my code
import matplotlib.pyplot as plt
import numpy as np
import cv2
def normalizeImage(image):
print("Normalizing image..")
min = np.min(image)
image = image-min #to have only positive values
max=np.max(image)
div=max/255 #calculate the normalize divisor
image_8u = np.uint8(np.round(image / div))
return image_8u
def detectBlobs(dustImage):
print("Detecting blobs...")
pp = cv2.SimpleBlobDetector_Params()
pp.filterByArea = True# Set Area filtering parameters
#minDiameter = 1
#maxDiameter = 5
pp.minArea = 100 #3.14159 * minDiameter * minDiameter
#pp.maxArea = 3.14159 * maxDiameter * maxDiameter
pp.filterByCircularity = True # Set Circularity filtering parameters
pp.minCircularity = 0.9
pp.filterByConvexity = True # Set Convexity filtering parameters
pp.minConvexity = 0.2
pp.filterByInertia = True # Set inertia filtering parameters
pp.minInertiaRatio = 0.1
detector = cv2.SimpleBlobDetector_create(pp) # Create a detector with the parameters
keypoints = detector.detect(dustImage) # Detect blobs
numberOfBlobs = len(keypoints)
if(numberOfBlobs > 0):
print(numberOfBlobs + "blobs detected!")
blank = np.zeros((1, 1))# Draw blobs on our image as red circles
blobs = cv2.drawKeypoints(dustImage, keypoints, blank, (0, 0, 255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
text = "Number of Circular Blobs: " + str(len(keypoints))
cv2.putText(blobs, text, (20, 550), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 100, 255), 3)
# Show blobs
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.resizeWindow("img", 900 ,900)
cv2.imshow("img", blobs)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print('No blobs detected')
return numberOfBlobs
if __name__ == '__main__':
img0 = plt.imread('artificial.png')
#plt.imshow(img0)
#plt.show()
img1 = normalizeImage(img0)
img2 = cv2.cvtColor(img1, cv2.CV_8UC1)
detectBlobs(img2)
exit()

Resizing an object in an image

What I want to do is to increase the size of these boxes inplace, like if the box has a size of 100x200 i want to be 120x240 ie a 20 percent increase in size.
Resizing an croping the image will not work for all images as I am using it as a mask for another image(s) and if the position is changed the next step will not work.
I have been searching for a way to do it but was unable to find it.
I am using python 3.9.4
You can use connected components function from OpenCV to detect the white boxes. Now once you the center, height, width of all the boxes you can simply increase the size of them and replace them on a black image.
Official documentation of the function : Structural Analysis and Shape Descriptors
import cv2
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,
cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
output = cv2.connectedComponentsWithStats(thresh, args["connectivity"],cv2.CV_32S)
(numLabels, labels, stats, centroids) = output
The above code loaded the image and finds out all the components in the image
now you can iterate over them to find the required component.
# loop over the number of unique connected component labels
for i in range(0, numLabels):
# if this is the first component then we examine the
# *background* (typically we would just ignore this
# component in our loop)
if i == 0:
text = "examining component {}/{} (background)".format(
i + 1, numLabels)
# otherwise, we are examining an actual connected component
else:
text = "examining component {}/{}".format( i + 1, numLabels)
# print a status message update for the current connected
# component
print("[INFO] {}".format(text))
# extract the connected component statistics and centroid for
# the current label
x = stats[i, cv2.CC_STAT_LEFT]
y = stats[i, cv2.CC_STAT_TOP]
w = stats[i, cv2.CC_STAT_WIDTH]
h = stats[i, cv2.CC_STAT_HEIGHT]
area = stats[i, cv2.CC_STAT_AREA]
(cX, cY) = centroids[i]

How to detect a grainy line?

I am trying to detect a grainy printed line on a paper with cv2. I need the angle of the line. I dont have much knowledge in image processing and I only need to detect the line. I tried to play with the parameters but the angle is always detected wrong. Could someone help me. This is my code:
import cv2
import numpy as np
import matplotlib.pylab as plt
from matplotlib.pyplot import figure
img = cv2.imread('CamXY1_1.bmp')
crop_img = img[100:800, 300:900]
blur = cv2.GaussianBlur(crop_img, (1,1), 0)
ret,thresh = cv2.threshold(blur,150,255,cv2.THRESH_BINARY)
gray = cv2.cvtColor(thresh,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 60, 150)
figure(figsize=(15, 15), dpi=150)
plt.imshow(edges, 'gray')
lines = cv2.HoughLines(edges,1,np.pi/180,200)
for rho,theta in lines[0]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 3000*(-b))
y1 = int(y0 + 3000*(a))
x2 = int(x0 - 3000*(-b))
y2 = int(y0 - 3000*(a))
cv2.line(img,(x1,y1),(x2,y2),(0, 255, 0),2)
imagetobedetected
Here's a possible solution to estimate the line (and its angle) without using the Hough line transform. The idea is to locate the start and ending points of the line using the reduce function. This function can reduce an image to a single column or row. If we reduce the image we can also get the total SUM of all the pixels across the reduced image. Using this info we can estimate the extreme points of the line and calculate its angle. This are the steps:
Resize your image because it is way too big
Get a binary image via adaptive thresholding
Define two extreme regions of the image and crop them
Reduce the ROIs to a column using the SUM mode, which is the sum of all rows
Accumulate the total values above a threshold value
Estimate the starting and ending points of the line
Get the angle of the line
Here's the code:
# imports:
import cv2
import numpy as np
import math
# image path
path = "D://opencvImages//"
fileName = "mmCAb.jpg"
# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Scale your BIG image into a small one:
scalePercent = 0.3
# Calculate the new dimensions
width = int(inputImage.shape[1] * scalePercent)
height = int(inputImage.shape[0] * scalePercent)
newSize = (width, height)
# Resize the image:
inputImage = cv2.resize(inputImage, newSize, None, None, None, cv2.INTER_AREA)
# Deep copy for results:
inputImageCopy = inputImage.copy()
# Convert BGR to grayscale:
grayInput = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# Adaptive Thresholding:
windowSize = 51
windowConstant = 11
binaryImage = cv2.adaptiveThreshold(grayInput, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, windowSize, windowConstant)
The first step is to get the binary image. Note that I previously downscaled your input because it is too big and we don't need all that info. This is the binary mask:
Now, we don't need most of the image. In fact, since the line is across the whole image, we can only "trim" the first and last column and check out where the white pixels begin. I'll crop a column a little bit wider, though, so we can ensure we have enough data and as less noise as possible. I'll define two Regions of Interest (ROIs) and crop them. Then, I'll reduce each ROI to a column using the SUM mode, this will give me the summation of all intensity across each row. After that, I can accumulate the locations where the sum exceeds a certain threshold and approximate the location of the line, like this:
# Define the regions that will be cropped
# from the original image:
lineWidth = 5
cropPoints = [(0, 0, lineWidth, height), (width-lineWidth, 0, lineWidth, height)]
# Store the line points here:
linePoints = []
# Loop through the crop points and
# crop de ROI:
for p in range(len(cropPoints)):
# Get the ROI:
(x,y,w,h) = cropPoints[p]
# Crop the ROI:
imageROI = binaryImage[y:y+h, x:x+w]
# Reduce the ROI to a n row x 1 columns matrix:
reducedImg = cv2.reduce(imageROI, 1, cv2.REDUCE_SUM, dtype=cv2.CV_32S)
# Get the height (or lenght) of the arry:
reducedHeight = reducedImg.shape[0]
# Define a threshold and accumulate
# the coordinate of the points:
threshValue = 100
pointSum = 0
pointCount = 0
for i in range(reducedHeight):
currentValue = reducedImg[i]
if currentValue > threshValue:
pointSum = pointSum + i
pointCount = pointCount + 1
# Get average coordinate of the line:
y = int(accX / pixelCount)
# Store in list:
linePoints.append((x, y))
The red rectangles show the regions I cropped from the input image:
Note that I've stored both points in the linePoints list. Let's check out our approximation by drawing a line that connects both points:
# Get the two points:
p0 = linePoints[0]
p1 = linePoints[1]
# Draw the line:
cv2.line(inputImageCopy, (p0[0], p0[1]), (p1[0], p1[1]), (255, 0, 0), 1)
cv2.imshow("Line", inputImageCopy)
cv2.waitKey(0)
Which yields:
Not bad, huh? Now that we have both points, we can estimate the angle of this line:
# Get angle:
adjacentSide = p1[0] - p0[0]
oppositeSide = p0[1] - p1[1]
# Compute the angle alpha:
alpha = math.degrees(math.atan(oppositeSide / adjacentSide))
print("Angle: "+str(alpha))
This prints:
Angle: 0.534210901840831

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])

How to find max RGB values in a circle using OpenCV?

I have a picture with 9 water droplets that have different color intensities (i.e. they are all green, but different shades of green). The goal is to:
Identify 9 drops
Find relevant values (size, location, RGB values, etc.)
Plot data
I am using SimpleBlobDetector to identify the dots. This outputs the keypoints, which contains relevant information about each blob.
However, I do not know how to access the RGB (or HSV) values for the specific blob. How do you search only the pixels in the blob to determine min/max/avg color values?
Any advice is greatly appreciated!
Here is my full code. It just prints the x_position, y_position, and area of each blob. I've also attached the file I am using:
# Standard imports
import cv2
import numpy as np
from matplotlib import pyplot as plt
# Read image
filename= "C:\Users\Kevin\Pictures\Far 3.jpg"
img = cv2.imread(filename, 0)
img_color = cv2.imread(filename, cv2.IMREAD_ANYCOLOR)
img_c = cv2.resize(img_color,(800,600))
img1 = cv2.resize(img,(800,600))
ret,im = cv2.threshold(img1,120,255,cv2.THRESH_BINARY)
#######################################################
#######################################################
# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 50
params.maxThreshold = 150
# Filter by Area.
params.filterByArea = True
params.minArea = 150
params.maxArea = 400
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0.2
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.1
# Filter by Inertia
params.filterByInertia = True
params.minInertiaRatio = 0.01
detector = cv2.SimpleBlobDetector_create(params)
#######################################################
#######################################################
# Detect blobs.
keypoints = detector.detect(im)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(img_c, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
x=[]
y=[]
area=[]
for i in xrange(9):
xx = keypoints[i].pt[0]
yy = keypoints[i].pt[1]
aarea = keypoints[i].size
print "PT.%f -- " %i, "x = %f," %xx, "y = %f," %yy,"area = %f," %aarea, "\n"
#######################################################
#######################################################
cv2.waitKey(0)
For each pixel in img, the B,G,R values can be read as:
B=img[xx,yy,0]
G=img[xx,yy,1]
R=img[xx,yy,2]
You can take the average B,G,R value of all the pixels in a blob and then find the maximum value among the blobs

Categories