CF11
CF11

Reputation: 109

How to scale down symbols in an image using Open CV

I'm trying to use Open CV to scale down numbers in an image. I currently am able to identify the contours but I am having trouble figuring out how to scale down the numbers once I have identified them.

Here is an example image: numbers

Here are the contours I have identified:

Contours

Here is the code I am using to achieve this:

import cv2

image = cv2.imread("numbers.png")
edged = cv2.Canny(image, 10, 250)


# applying closing function
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
closed = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, kernel)



_, cnts,_ = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, 
cv2.CHAIN_APPROX_SIMPLE)
contours = []


for c in cnts:

    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    contours.append(approx)

    cv2.drawContours(image, [approx], -1, (0, 255, 0), 2)


cv2.imshow("Output", image)
cv2.waitKey(0)

I want to be able to use the contours to scale down the numbers without affecting the size of the image. Is this possible? Thanks!

Upvotes: 3

Views: 299

Answers (1)

Howard GENG
Howard GENG

Reputation: 1105

Assuming you have an input image named "numbers.png". enter image description here

First of all, import useful libraries and load the input image:

import cv2
import numpy as np

img = cv2.imread("./numbers.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

Secondly, you need to binarize the input image and find the external contours of the numbers:

_, im_th = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
_, contours, _ = cv2.findContours(255-im_th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

So you can see the detected contours will be around the numbers. enter image description here

Thirdly, find the relative bounding boxes around the numbers and find the middle point coordinates of the boxes (I assume the numbers should be resized and put in the center of the bottom line):

number_imgs = []
number_btm_mid_pos = []
for cnt in contours:
    (x, y, w, h) = cv2.boundingRect(cnt)
    number_imgs.append(img[y:y+h, x:x+w])
    number_btm_mid_pos.append((int(x+w/2), y+h))

Finally, resize the numbers, put them back to the image, and display the result:

# resize images and put it back
output_img = np.ones_like(img) * 255
resize_ratio = 0.5
for (i, num_im) in enumerate(number_imgs):
    num_im = cv2.resize(num_im, (0,0), fx=resize_ratio, fy=resize_ratio)
    (img_h, img_w) = num_im.shape[:2]
    # x1, y1, x2, y2
    btm_x, btm_y = number_btm_mid_pos[i]
    x1 = btm_x - int(img_w / 2)
    y1 = btm_y - img_h
    x2 = x1 + img_w
    y2 = y1 + img_h
    output_img[y1:y2, x1:x2] = num_im

cv2.imshow("Output Image", output_img)
cv2.imshow("Original Input", img)
cv2.waitKey()

You can adjust the variable "resize_ratio" to make sure the ratio is what you expected. The result should be something like this image here: enter image description here

You may notice the last number "10" is splitting apart. It is because the "1 0" was recognized as two separate digits. To make it perfect, it is possible to write some code to test the gap/distance between every two digits. However, that would be not closely relevant, and a bit hard to generalize the solution based on the limited testing input. So I stop here.

Anyway, good luck and have fun.

Upvotes: 5

Related Questions