I must extract a signature from a blank scanned image containing only the signature, which can be placed everywhere. The image may be also a smartphone picture.
I realized that OpenCV can fit well but I’m facing some problems.
I’m using the HSV analysis approach:
- I defined the color threshold for the lower and upper bounds.
- Exctacted a mask corresponding to the bounds.
- Called the find contours method to get all the contours corresponding to the mask.
- Merged all the contours matching the mask.
- Extracted the signature.
Here is the problem: the bbox of the signature is smaller than what it is expected, the signature is recognized but the bbox borders do not cover the full signature.
Here is the code:
public static synchronized void extractSignature(File signature) {
if (signature == null)
return;
String fileExtension = FilenameUtils.getExtension(signature.getName());
// check if the file extension is correct!
if (!fileExtension.toLowerCase().equals("jpg") && !fileExtension.toLowerCase().equals("jpeg") && !fileExtension.toLowerCase().equals("png")) {
return;
}
try {
if (!isLoaded) {
// System.loadLibrary(Core.NATIVE_LIBRARY_NAME); // not working
// OpenCV.loadShared(); // not working
OpenCV.loadLocally();
isLoaded = true;
}
} catch (Exception e) {
System.out.println(e.toString());
}
// Load image
Mat image = Imgcodecs.imread(signature.getPath());
Mat originalImage = image.clone();
// Convert image to HSV color space
Mat hsv = new Mat();
Imgproc.cvtColor(image, hsv, Imgproc.COLOR_BGR2HSV);
// Define lower and upper bounds for color threshold
Scalar lower = new Scalar(90, 38, 22); // original 90 38 0
Scalar upper = new Scalar(255, 255, 255); // original 145 255 255
// Threshold the HSV image to get only desired colors
Mat mask = new Mat();
Core.inRange(hsv, lower, upper, mask);
// Find contours
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(mask, contours, hierarchy, Imgproc.RETR_LIST , Imgproc.CHAIN_APPROX_SIMPLE);
// Combine all contours into one and get bounding box
MatOfPoint allContours = new MatOfPoint();
int i = 0;
for (MatOfPoint contour : contours) {
List<Point> pts = contour.toList();
System.out.println(i + " -> " + pts.toString());
allContours.push_back(new MatOfPoint(Converters.vector_Point_to_Mat(pts)));
i++;
}
Rect boundingBox = Imgproc.boundingRect(allContours);
// Apply Green box to the ROI from the original image using the bounding box coordinates
Imgproc.rectangle(image, boundingBox.tl(), boundingBox.br(), new Scalar(36, 255, 12), 2);
// Add some pixels to each dimension of the bounding box for the final extraction
int padding = 150;
int x = Math.max(boundingBox.x - 150, 0); // width
int y = Math.max(boundingBox.y - 55, 0); // height
int width = Math.min(boundingBox.width + 2 * 150, image.cols() - x);
int height = Math.min(boundingBox.height + 2 * 55, image.rows() - y);
Rect paddedBoundingBox = new Rect(x, y, width, height);
// Extract ROI
// Mat ROI = new Mat(imageOriginal, boundingBox); // bbox without padding
Mat ROI = new Mat(originalImage, paddedBoundingBox); // bbox with padding
// Convert ROI to grayscale
Mat grayROI = new Mat();
Imgproc.cvtColor(ROI, grayROI, Imgproc.COLOR_BGR2GRAY);
// Convert to B/W
Imgproc.threshold(ROI, ROI, 150, 255, Imgproc.THRESH_BINARY);
// Save and display images
//Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator+ "result.jpg", result);
Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator + "image_with_bbox.jpg", image);
Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator + signature.getName(), ROI);
Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator + "grayROI.jpg", grayROI);
return;
}
To avoid a bit of the problem I manually increased the BBOX, but it is not a solution. I’m new to OpenCV so whatever suggestion would be highly appreciated even a completely new approach.
Original Image + Green BBOX extracted