使用opencv获取图像中所有文本的位置


11

我有此图像,其中包含文本(数字和字母)。我想获取此图像中存在的所有文本和数字的位置。我也想提取所有文本。

在此处输入图片说明

如何获取图像中的坐标以及所有文本(数字和字母)。例如10B,44、16、38、22B等


你的tensorflow版本是什么?如果您的版本是2.1,请尝试安装2.0
gellezzz

1
向不好的问题投掷赏金不是好习惯。您没有显示如何执行此操作的知识,因此您似乎只是在试图吸引开发人员编写完整的解决方案,以换取一些代表点。出于这个原因,我并不希望看到完美的答案,但是我相信,如果您愿意花钱给别人,您可以在自由职业者的网站上获得更好的解决方案。
karlphillip

@karlphillip很抱歉,但是我是一个初学者,我需要开始一些事情,对吗?您能帮我吗
Pulkit Bhatnagar

Answers:


13

这是一种使用形态学运算过滤掉非文本轮廓的潜在方法。这个想法是:

  1. 获取二进制图像。加载图像,灰度,然后 大津的阈值

  2. 删除水平和垂直线。使用创建水平和垂直内核,cv2.getStructuringElement然后使用cv2.drawContours

  3. 删除对角线,圆形对象和弯曲轮廓。使用轮廓区域cv2.contourArea 和轮廓近似cv2.approxPolyDP 进行过滤以隔离非文本轮廓

  4. 提取文本ROI和OCR。找到轮廓并过滤ROI,然后使用Pytesseract进行OCR 。


删除以绿色突出显示的水平线

在此处输入图片说明

删除垂直线

在此处输入图片说明

删除了各种非文本轮廓(对角线,圆形对象和曲线)

在此处输入图片说明

检测到的文字区域

在此处输入图片说明

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
clean = thresh.copy()

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,30))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

cnts = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    # Remove diagonal lines
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(clean, [c], -1, 0, 3)
    # Remove circle objects
    elif area > 1000:
        cv2.drawContours(clean, [c], -1, 0, -1)
    # Remove curve stuff
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    x,y,w,h = cv2.boundingRect(c)
    if len(approx) == 4:
        cv2.rectangle(clean, (x, y), (x + w, y + h), 0, -1)

open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
opening = cv2.morphologyEx(clean, cv2.MORPH_OPEN, open_kernel, iterations=2)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,2))
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, close_kernel, iterations=4)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = cv2.contourArea(c)
    if area > 500:
        ROI = image[y:y+h, x:x+w]
        ROI = cv2.GaussianBlur(ROI, (3,3), 0)
        data = pytesseract.image_to_string(ROI, lang='eng',config='--psm 6')
        if data.isalnum():
            cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
            print(data)

cv2.imwrite('image.png', image)
cv2.imwrite('clean.png', clean)
cv2.imwrite('close.png', close)
cv2.imwrite('opening.png', opening)
cv2.waitKey()

最好先删除这些行。
karlphillip

还要删除部分字母的好主意...
Walter Tross

8

好了,这是另一个可能的解决方案。我知道您使用Python-我使用C ++。我会给您一些想法,并希望,如果您愿意,您将能够实现此答案。

主要思想是根本不使用预处理(至少在初始阶段不使用),而是专注于每个目标字符,获取一些属性,并根据这些属性过滤每个blob。

我尝试不使用预处理,因为:1)过滤器和形态阶段可能会降低斑点的质量; 2)目标斑点似乎表现出我们可以利用的一些特征,主要是:长宽比面积

检查一下,数字和字母似乎都比宽高高……此外,它们似乎在某个面积值内变化。例如,您要丢弃对象“太宽”“太大”

我的想法是,我将过滤掉不属于预先计算的值的所有内容。我检查了字符(数字和字母),并附带了最小,最大面积值和最小纵横比(此处为高与宽之间的比率)。

让我们来研究算法。首先读取图像并将其调整为一半尺寸。您的图片太大了。转换为灰度并通过otsu获取二进制图像,这是伪代码:

//Read input:
inputImage = imread( "diagram.png" );

//Resize Image;
resizeScale = 0.5;

inputResized = imresize( inputImage, resizeScale );

//Convert to grayscale;
inputGray = rgb2gray( inputResized );

//Get binary image via otsu:
binaryImage = imbinarize( inputGray, "Otsu" );

凉。我们将使用此图像。您需要检查每个白色斑点,然后应用“属性过滤器”。我使用带有统计信息的连接组件来循环遍历每个Blob并获取其面积和纵横比,在C ++中,此操作如下:

//Prepare the output matrices:
cv::Mat outputLabels, stats, centroids;
int connectivity = 8;

//Run the binary image through connected components:
int numberofComponents = cv::connectedComponentsWithStats( binaryImage, outputLabels, stats, centroids, connectivity );

//Prepare a vector of colors  color the filtered blobs in black
std::vector<cv::Vec3b> colors(numberofComponents+1);
colors[0] = cv::Vec3b( 0, 0, 0 ); // Element 0 is the background, which remains black.

//loop through the detected blobs:
for( int i = 1; i <= numberofComponents; i++ ) {

    //get area:
    auto blobArea = stats.at<int>(i, cv::CC_STAT_AREA);

    //get height, width and compute aspect ratio:
    auto blobWidth = stats.at<int>(i, cv::CC_STAT_WIDTH);
    auto blobHeight = stats.at<int>(i, cv::CC_STAT_HEIGHT);
    float blobAspectRatio = (float)blobHeight/(float)blobWidth;

    //Filter your blobs

};

现在,我们将应用属性过滤器。这只是与预先计算的阈值的比较。我使用以下值:

Minimum Area: 40  Maximum Area:400
MinimumAspectRatio:  1

for循环内部,将当前的Blob属性与这些值进行比较。如果测试为阳性,则将“斑点”涂成黑色。在for循环内继续:

    //Filter your blobs

    //Test the current properties against the thresholds:
    bool areaTest =  (blobArea > maxArea)||(blobArea < minArea);
    bool aspectRatioTest = !(blobAspectRatio > minAspectRatio); //notice we are looking for TALL elements!

    //Paint the blob black:
    if( areaTest || aspectRatioTest ){
        //filtered blobs are colored in black:
        colors[i] = cv::Vec3b( 0, 0, 0 );
    }else{
        //unfiltered blobs are colored in white:
        colors[i] = cv::Vec3b( 255, 255, 255 );
    }

循环之后,构造过滤的图像:

cv::Mat filteredMat = cv::Mat::zeros( binaryImage.size(), CV_8UC3 );
for( int y = 0; y < filteredMat.rows; y++ ){
    for( int x = 0; x < filteredMat.cols; x++ )
    {
        int label = outputLabels.at<int>(y, x);
        filteredMat.at<cv::Vec3b>(y, x) = colors[label];
    }
}

而且……差不多。您过滤了所有与要查找的元素不相似的元素。运行算法,您将得到以下结果:

在此处输入图片说明

我还找到了斑点的边界框,以更好地可视化结果:

在此处输入图片说明

如您所见,某些元素被漏检。您可以优化“属性过滤器”以更好地识别所需的字符。更深层次的解决方案需要一点机器学习,因此需要构建“理想特征向量”,从斑点中提取特征,并通过相似性度量比较这两个向量。您还可以应用一些后期处理以改善结果...

不管怎样,伙计,您的问题不是小事,也不是容易扩展的,我只是在给您一些想法。希望您能够实现您的解决方案。


您有机会将相同程序转换为python
Pulkit Bhatnagar

@PulkitBhatnagar是的,当然。您只要紧紧握紧,几分钟后我会准备好一个完美的端口。
eldesgraciado

?? 您是否这样做了,以便我可以向您授予赏金
Pulkit Bhatnagar

是的。很抱歉,先生,我遇到了一些麻烦,但是转换进展顺利。等你 谢谢。
eldesgraciado

没想到这会是一种讽刺。
Pulkit Bhatnagar

4

一种方法是使用滑动窗口(价格昂贵)。

确定图像中字符的大小(所有字符的大小均与图像中看到的大小相同)并设置窗口的大小。尝试使用tesseract进行检测(输入图像需要预处理)。如果窗口连续检测到字符,则存储窗口的坐标。合并坐标并获得字符上的区域。


我认为答案是100bounty
Himanshu Poddar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.