Pulling a letter out of a picture

Greetings to all. I have been suffering for 2 days. I need to get these letters out of the picture, but I can't figure out how.

What I tried and it somehow works (through time and then with tension):

    screen = cv2.imread(img_path)

    screen = cv2.resize(screen, (100, 100))

    gray = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY)
    gray = cv2.bilateralFilter(gray, 11, 17, 17)
    edged = cv2.Canny(gray, 30, 200)

    cnts = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)

    for c in cnts:

        peri = cv2.arcLength(c, True)
        approx = cv2.approxPolyDP(c, 0.015 * peri, True)

        if len(approx) > 0:
            screenCnt = approx
            cv2.drawContours(screen, [screenCnt], -1, (0, 255, 0), 3)

            break

In general, ideally, I wanted to do something like this:

What is there:

What I have

What I need:

What do you need

What the code "sees" :

enter a description of the image here

It seems to be good, but if you take another image with a square that slightly overlaps the letter, then he sees this already:

enter a description of the image here

What can I do about it?

The main thing is to get the letter out of it. To be honest, I don't know what to do with it anymore.

Author: MaxU, 2020-07-10

2 answers

import cv2
import numpy

#file = "pFgR2.jpg"
file = "whyJd.jpg"

original = cv2.imread(file)
miniature = cv2.resize(original, (100, 100))
gray = cv2.cvtColor(miniature, cv2.COLOR_BGR2GRAY)

(_ret, threshold) = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY)

# Бинаризация отсекла букву от фона, выделив фон белым.
# Нам же нужно обратное.
threshold = 255 - threshold

(contours, _) = cv2.findContours(threshold, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# Ищем квадрат с буквой.
# Задаем начальные максимальные и минимальные координаты квадрата.
# Осторожно! Ниже только целочисленные значения от 0 до 255.
mins = numpy.array((255, 255), numpy.uint8)
maxs = numpy.array((0, 0), numpy.uint8)

# Проходимся по квадратам контуров.
for rect in contours:
    (x, y, w, h)=cv2.boundingRect(rect)
    
    if x <= 0 or y <= 0:

        # «Глобальный» контур охватывает все изображение,
        # полностью портя вычисление квадрата с буквой.
        continue
    
    # Обновляем координаты квадрата с буквой с учетом квадрата каждого контура.
    maxs = numpy.maximum(maxs, (x + w, y + h))
    mins = numpy.minimum(mins, (x, y))
    
# Теперь мы можем получить итоговое изображение только с буквой.

# Если захотите увидеть контуры буквы на итоговом изображении.
#cv2.drawContours(miniature, contours, -1, (0, 255, 0), 2)

essence = miniature[ mins[1]:maxs[1], mins[0]:maxs[0] ]

cv2.imshow("essence", essence)
cv2.waitKey(0)

There were some developments on this topic. Adjusted it to your task. With the images provided by you, it copes at the level you need.


The coordinates of the square with the letter can also be found using cv2.MinMaxLoc. The main problem remains in ignoring the "global" contour that covers the entire image. At the time, I did not solve this problem and wrote in the way above.

 4
Author: Shamus Rezol, 2020-07-11 07:03:29

You can simply fill in the frame on the binarized image, so that only the characters remain. And then find the position of the letters. There is no need to select the contours.

import cv2
import numpy as np

file = "pFgR2.jpg"
#file = "whyJd.jpg"

original = cv2.imread(file)
miniature = cv2.resize(original, (100, 100))
gray = cv2.cvtColor(miniature, cv2.COLOR_BGR2GRAY)
(_ret, threshold) = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)
h, w = threshold.shape[:2]
threshold = cv2.rectangle(threshold,(0,0),(w,h),(255),2)
mask = np.zeros((h+2, w+2), np.uint8)
cv2.floodFill(threshold, mask, (0,0), 0);
y_nonzero, x_nonzero = np.nonzero(threshold)
essence=miniature[np.min(y_nonzero):np.max(y_nonzero), np.min(x_nonzero):np.max(x_nonzero),:]
cv2.imshow('essence', essence)
cv2.waitKey(0)
 0
Author: Alex Alex, 2020-08-27 04:59:50