Karan Kanwal
Karan Kanwal

Reputation: 39

How to extract signature from an image (python script)?

Here is a sample image of signature :

sample

How to get the signature from this image without background so that I can paste it over user image. What if background is not white?

I have tried this, how to customize it for different background colors?

Upvotes: 0

Views: 8256

Answers (3)

J.D.
J.D.

Reputation: 4561

It is quite a process, mainly because there are a number of steps to add an image on top of an image of a different size. I advice you to check out all intermediate steps in the code below, to understand what happens.

I used the HSV-colorspace to separate the signature from the background, this is easy to adapt if the signature or background have other colors.

I have not found python bindings for the copyTo()-method used by @BahramdunAdil. You could use numpy.copyto() functionality instead. For that I'll refer you to this answer.

I used a different technique: to add the image on top of another, first a subimage of the same size as the signature is created. The signature can be added to the subimage, which is then put back in the main image.

Alternatively, you can take the thresholded signature and use @renedv1's method to save an alpha image. Use the sign_masked image for that. Because of the HSV-range you can create a cleaner result. (Note: account for the fact that sign_masked has a black background)

Result:
enter image description here

Code:

    import numpy as np 
    import cv2
    # load image
    sign = cv2.imread("sign.jpg")
    bg_img = cv2.imread("green_area.jpg")

     # Convert BGR to HSV
    hsv = cv2.cvtColor(sign, cv2.COLOR_BGR2HSV)

    # define range of HSV-color of the signature
    lower_val = np.array([0,0,0])
    upper_val = np.array([179,255,150])

    # Threshold the HSV image to get a mask that holds the signature area
    mask = cv2.inRange(hsv, lower_val, upper_val)
    # create an opposite: a mask that holds the background area
    mask_inv= cv2.bitwise_not(mask)

    # create an image of the signature with background excluded
    sign_masked = cv2.bitwise_and(sign,sign,mask=mask)

    # get the dimensions of the signature
    height, width = sign.shape[:2]

    # create a subimage of the area where the signature needs to go
    placeToPutSign = bg_img[0:height,0:width]
    # exclude signature area 
    placeToPutSign_masked = cv2.bitwise_and(placeToPutSign, placeToPutSign, mask=mask_inv)
    # add signature to subimage
    placeToPutSign_joined = cv2.add(placeToPutSign_masked, sign_masked)

    # put subimage over main image
    bg_img[0:height,0:width] = placeToPutSign_joined

    # display image
    cv2.imshow("result", bg_img)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

Upvotes: 1

renedv1
renedv1

Reputation: 131

Just started with Python myself but thought I'd have a go at a solution - came up with this:

#!/usr/bin/python2

import cv2
import numpy as np

file_name = "/tmp/signature.jpg" # your signature image...

image = cv2.imread(file_name, 1)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGBA)
# note: [R,G,B,255] below, so first 3 numbers [255,255,255] are your white 
# background pixels to be converted to RGBA setting of [0,0,0,0] (transparent)
image[np.all(image == [255, 255, 255, 255], axis=2)] = [0, 0, 0, 0]

cv2.imwrite("/tmp/signature-transparent.png", image)

This script will grab your signature.jpg, make a transparent background from all the white pixels it finds, then write it to signature.png.

Looks like this:

transparent background image

However it's not exactly clean around the edges! Anyone out there who can sort that?

Upvotes: 3

Bahramdun Adil
Bahramdun Adil

Reputation: 6079

You should consider the steps below: for example, pretend it is your user image:

enter image description here

Now go with these steps:

cv::namedWindow("result", cv::WINDOW_FREERATIO);
cv::Mat signatureImg = cv::imread(R"(izrMq.jpg)");
cv::Mat userImg = cv::imread(R"(user_image.jpg)");

// make a mask
cv::Mat mask;
cv::cvtColor(signatureImg, mask, cv::COLOR_BGR2GRAY);
cv::threshold(mask, mask, 150, 255, cv::THRESH_BINARY_INV);

// now copy
cv::Mat submat = userImg(cv::Rect(userImg.cols-signatureImg.cols, userImg.rows-signatureImg.rows, signatureImg.cols, signatureImg.rows));
signatureImg.copyTo(submat, mask);

cv::imshow("result", userImg);

cv::waitKey();

And it is the result:

enter image description here

Hope it helps!

Upvotes: 0

Related Questions