from PIL import Image
import pytesseract
import argparse
import cv2

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the image")
args = vars(ap.parse_args())

# load the image and convert it to grayscale
image = cv2.imread(args["image"])
cv2.imshow("Original", image)

# Apply an "average" blur to the image

blurred = cv2.blur(image, (3,3))
cv2.imshow("Blurred_image", blurred)
img = Image.fromarray(blurred)
text = pytesseract.image_to_string(img, lang='eng')
print (text)
cv2.waitKey(0)

As as result i get = "Stay: in an Overwoter Bungalow $3»"

What about using Contour and taking unnecessary blobs from it ? might work

Answer from Deepan Raj on Stack Overflow
Discussions

python - How can we read a blurred image using pytesseract? - Stack Overflow
I am using pytesseract ocr to read the text of the image, but i am getting a false output for blurry images shown below. How can I make the above image or such images ocr ready. More on stackoverflow.com
🌐 stackoverflow.com
opencv - Extract text from blurred rotated image in python? - Stack Overflow
I have lots of image like these. Basically it's rotated and blur in nature. I just want to extract the table that contains items, description and sale. Can you please help me, how to extract that t... More on stackoverflow.com
🌐 stackoverflow.com
February 12, 2022
python - pytesseract improving OCR accuracy for blurred numbers on an image - Stack Overflow
The idea is to obtain a processed image where the text to extract is in black with the background in white. To do this, we can convert to grayscale, then apply a sharpening kernel using cv2.filter2D() to enhance the blurred sections. A general sharpening kernel looks like this: ... Other kernel variations can be found here. Depending on the image, you can adjust the strength of the filter. From ... More on stackoverflow.com
🌐 stackoverflow.com
django - How to improve OCR extraction from low-contrast and blurred newspaper images using Python and Tesseract? - Stack Overflow
I am working on a Django application to extract text from images of newspaper clippings. These images often have low contrast and blurring, and contain various text blocks such as headings, dates, ... More on stackoverflow.com
🌐 stackoverflow.com
🌐
Aisangam
aisangam.com › blog › ocr-extract-characters-in-blur-image-using-python-ai-sangam
OCR | Extract Character’s in Blur Image using Python | AI Sangam – AI SANGAM
But before going further, I would ... video from this link to get more familiar with Optical Character Recognition and how it is done in python · Optical Character Recognition using Python | AI SANGAM · Limitation of such work already implemented is the above code in the video is not working on the images where blur was present so we came with the blog and write something about removing the blur effect first and then extracting the text from the ...
🌐
Stack Overflow
stackoverflow.com › questions › 58392260 › how-can-we-read-a-blurred-image-using-pytesseract
python - How can we read a blurred image using pytesseract? - Stack Overflow
I am using pytesseract ocr to read the text of the image, but i am getting a false output for blurry images shown below. How can I make the above image or such images ocr ready.
🌐
YouTube
youtube.com › al sangam
OCR | Extract Character's in Blur Image using Python | AI Sangam - YouTube
Optical Character Recognition in Blur Image using image processing algorithms in pythonFor more Queries or any help visit us at https://www.aisangam.com/or y...
Published   September 7, 2018
Views   2K
🌐
Cloudinary
cloudinary.com › home › how to use ocr text recognition to automatically transform images
How to use OCR Text Recognition to automatically transform images
April 19, 2017 - Learn how to detect and extract text from images, process it, and automatically apply blur, pixelate, text overlay or crop effects.
🌐
Stack Overflow
stackoverflow.com › questions › 71094454 › extract-text-from-blurred-rotated-image-in-python
opencv - Extract text from blurred rotated image in python? - Stack Overflow
February 12, 2022 - Basically it's rotated and blur in nature. I just want to extract the table that contains items, description and sale. Can you please help me, how to extract that table from these type of image. I have tried, but it's works for clear and non-rotated image. pytesseract.pytesseract.tesseract_cmd = r'C:/Program Files/Tesseract-OCR/tesseract.exe' text = pytesseract.image_to_string(Image.open(d), lang="eng") print(text)
Find elsewhere
🌐
Medium
medium.com › @johnidouglasmarangon › ocr-tools-my-latest-study-solving-real-world-problems-with-low-quality-images-dce26cbcdf9a
OCR Tools — My Latest Study: Solving Real-World Problems with Low-Quality Images | by Johni Douglas Marangon | Medium
January 29, 2025 - When it comes to extracting text from low-quality images, DocTR (Document Text Recognition) stands out as a robust open-source solution. Built on top of deep learning frameworks like TensorFlow and PyTorch, DocTR provides state-of-the-art ...
Top answer
1 of 1
6

Here's a simple approach using OpenCV and Pytesseract OCR. To perform OCR on an image, it's important to preprocess the image. The idea is to obtain a processed image where the text to extract is in black with the background in white. To do this, we can convert to grayscale, then apply a sharpening kernel using cv2.filter2D() to enhance the blurred sections. A general sharpening kernel looks like this:

[[-1,-1,-1], [-1,9,-1], [-1,-1,-1]]

Other kernel variations can be found here. Depending on the image, you can adjust the strength of the filter. From here we Otsu's threshold to obtain a binary image then perform text extraction using the --psm 6 configuration option to assume a single uniform block of text. Take a look here for more OCR configuration options.


Here's a visualization of the image processing pipeline:

Input image

Convert to grayscale -> apply sharpening filter

Otsu's threshold

Result from Pytesseract OCR

124,685

Code

import cv2
import numpy as np
import pytesseract

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

# Load image, grayscale, apply sharpening filter, Otsu's threshold 
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
sharpen_kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
sharpen = cv2.filter2D(gray, -1, sharpen_kernel)
thresh = cv2.threshold(sharpen, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# OCR
data = pytesseract.image_to_string(thresh, lang='eng', config='--psm 6')
print(data)

cv2.imshow('sharpen', sharpen)
cv2.imshow('thresh', thresh)
cv2.waitKey()
🌐
Image to Text
imagetotext.io
Image to Text Converter - Extract Text From Image
Click the “Submit and Extract” button, and our Image to Text Converter will automatically extract text from images and transform it into editable text. Imagetotext.info helped us to automate this process. ... Save your text using the “Download” icon ( ) or copy it straight to your clipboard with the “Copy” Icon ( ). Online Image to Text Converter converts any images into editable text. It is developed using OCR (Optical Character Recognition), Tesseract, and other Python libraries for accurate photo to text conversion.
🌐
Cloudinary
cloudinary.com › home › extract text from images in python with pillow and pytesseract
Extract Text from Images in Python with Pillow and pytesseract | Cloudinary
November 23, 2025 - But let’s see some more powerful uses of image text extraction. Many images may have text, such as phone numbers, website addresses, license plates, or other personal data you don’t want to show on your website or application. To blur or pixelate all detected text in an image, you can use Cloudinary’s built-in pixelate_region or blur_region effect, with the gravity parameter set to ocr_text.
Top answer
1 of 1
2

It is a reasonable heuristic that most of the paper of a printed document is sort of white (or for old material yellow with orange spots). For this reason if you have a colour scan available then the red channel alone is often a much better higher contrast starting point. Always worth a look at the green but the blue channel usually just adds noise and foxing marks and lowers contrast.

To make text more readable "equalise histogram" isn't really what you want to do to the monochrome image. What you actually want to do is smash all the histogram peak at the top end near white to 255 and the smaller peak at the bottom end to 0 and then linearly assign the N remaining intervening values to (255*k)/N. Exact choice of cutoff isn't sensitive but this step wrecks halftone images to make the text contrast more clear.

If that still isn't enough and the text remains blurred then unsharp masking matched to the blur determined empirically from the image usually works best rather than a crude "sharpen" kernel.

You should be able to avoid having to Gaussian blur the image entirely (a step which is only needed in your workflow because of the side effects of equalise histogram). If it does need any further noise pre treatment remove salt and pepper noise or a median filter or edge preserving smooth are all better choices.

Gaussian blur can help make scanned halftone images into something acceptable though.

Sometimes one method of denoising works much better than any of the others on a particularly messy page that has been through the wars. The workflow above should work reasonably well on most material but a few awkward cases will need additional manual intervention to get the best results.

🌐
Quora
quora.com › How-do-I-detect-blurry-text-in-an-image
How to detect blurry text in an image - Quora
Answer (1 of 2): I don’t know of any such plugin. But I wonder if simply reducing the image and then expanding that back to the original size, and comparing the result with the original might be useful. The idea is that with blurring, the high frequency information is gone.
🌐
LinkedIn
linkedin.com › pulse › how-extract-text-from-image-using-python-yamil-garcia
How to extract text from an image using Python?
July 3, 2023 - Noise can be reduced using techniques like Gaussian blur. Binarization (Thresholding): Binarization is the process of converting an image to black and white. This can help to make the text more distinguishable from the background. Dilation and Erosion: These operations can help increase the text size and remove noise. Deskewing: If the text in the image is skewed, straightening it can improve OCR results. Here's a Python script using OpenCV for pre-processing an image before extracting text with Pytesseract:
🌐
Affinda
affinda.com › home › blog › how to convert image to text using python
How to Convert Image to Text Using Python: A Comprehensive Guide for 2024
September 8, 2025 - Master image to text conversion in Python with our step-by-step guide. Learn to use Tesseract, OpenCV and easyOCR for accurate OCR.
Top answer
1 of 2
1

Tesseract can use the gradients around text as part of its detection, so I'd suggest you avoid thresholding where possible, as it removes the gradients (anti-aliasing, as mentioned by fmw42) from the image.

Instead here I'd suggest inverting the image after you grayscale it, and then if necessary you can reduce the brightness to make the more grey text a bit blacker, and increase the contrast to make the grey background a bit more white. If you do need to adjust the brightness and/or contrast I'd suggest using cv2.convertScaleAbs to do so efficiently and avoid integer overflow problems.

2 of 2
1
  • my text after applying some threshold effect is blurry

You applied simple-thresholding and did not achieve the desired result. The two missing parts are:

    1. You should try other pre-processing methods.
    1. You should try with other page-segmentation-modes

The other approach for thresholding is taking the binary mask and applying some morphological-transformation. Then you need to display each detected text-region, center the image and apply ocr. Using this approach, you can visualize and see where the problem is.

  • 1. Binary Mask

    • As we can see, the world and the background images were removed from the image.

  • 2. Dilation

    • We applied dilation and bitwise_and for detecing the text more accurately.

    1. Detecting text regions
    • Tesseract may find same-region multiple times. The idea is to find which part of the text is misinterpreted. Here are some examples:
Region Result
~ PLAN NACIONAL DE VACUNACION
CONTRA COVID-19 Fam>—
DOSIS APLICADAS: 191.480
CORTE 4:00 P.M. MARZO - 03 - 2021
YPROVIENGIA —» SANTA MARTA (9501 © — LA GUARA 11345)
BARRANGRILLA CS 997) ATLANTICD3 754) —- ° * —-— MAGDKLENACIS87)
GARTAGENA [3.457 }BOLIVAR (2.500) —, . CESAR (3.016)

I found the above results (some of them displayed) using english language. The current language is foreign to me. Therefore you need to configure the tesseract for the language

Code:


# Load the library
import cv2
import numpy as np
import pytesseract

# Load the image
img = cv2.imread("u1niV.jpg")

# Convert to HSV color-space
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Get binary mask
msk = cv2.inRange(hsv, np.array([0, 0, 181]), np.array([160, 255, 255]))

# Extract features
krn = cv2.getStructuringElement(cv2.MORPH_RECT, (5 ,3))
dlt = cv2.dilate(msk, krn, iterations=5)
res = 255 - cv2.bitwise_and(dlt, msk)

# OCR detection
d = pytesseract.image_to_data(res, output_type=pytesseract.Output.DICT)

# Get ROI part from the detection
n_boxes = len(d['level'])

# For each detected part
for i in range(n_boxes):

    # Get the localized region
    (x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i])

    # Draw rectangle to the detected region
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 5)

    # Crop the region
    cropped = res[y:y+h, x:x+w]

    # Center the region
    cropped = cv2.copyMakeBorder(cropped, 50, 50, 50, 50, cv2.BORDER_CONSTANT, value=255)

    # OCR the region
    txt = pytesseract.image_to_string(cropped)
    print(txt)

    # Display
    cv2.imshow("cropped", cropped)
    cv2.waitKey(0)

# Display
cv2.imshow("res", res)
cv2.waitKey(0)

Top answer
1 of 2
19

Are you aware of Blind deconvolution?

Blind deconvolution is a well-known technique in restoring astronomical images. This is specially useful for your application, where finding a PSF is difficult.

Here is one C++ implementation of this technique. This paper is also very related to what you are looking for. Here is a sample output of their algorithm:

2 of 2
12

I've also encountered this issue recently and raise a similar question with more details and with a recent approach. It seems to be an unsolved problem until now. There are some recent research works that try to address such problems with deep learning. Unfortunately, none of the works reach our expectations. However, I'm sharing the info in case it may come helpful to anyone.

1. Scene Text Image Super-Resolution in the Wild

In our case, it may be our last choice; comparatively, perform well enough. It's a recent research work (TSRN) mainly focuses on such cases. The main intuitive of it is to introduce super-resolution (SR) techniques as pre-processing. This implementation looks by far the most promising. Here is the illustration of their achievement, improve blur to clean image.

2. Neural Enhance

From their repo demonstration, It's appearing that It may have some potential to improve blur text either. However, the author probably doesn't maintain the repo for about 4 years.

3. Blind Motion Deblurring with GAN

The attractive part is the Blind Motion Deblurring mechanism in it, named DeblurGAN. It looks very promising.

4. Real-World Super-Resolution via Kernel Estimation and Noise Injection

An interesting fact about their work is that unlike other literary works they first design a novel degradation framework for realworld images by estimating various blur kernels as well as real noise distributions. Based on that they acquire LR images sharing a common domain with real-world images. Then, they propose a realworld super-resolution model aiming at better perception. From their article:

However, in my observation, I couldn't get the expected results. I've raised an issue on github and until now didn't get any response.


Convolutional Neural Networks for Direct Text Deblurring

The paper that was shared by @Ali looks very interesting and the outcomes are extremely good. It's nice that they have shared the pre-trained weight of their trained model and also shared python scripts for easier use. However, they've experimented with the Caffe library. I would prefer to convert into PyTorch to better control. Below are the provided python scripts with Caffe imports. Please note, I couldn't port it completely until now because of a lack of Caffe knowledge, please correct me if you are aware of it.

from __future__ import print_function
import numpy as np
import os, sys, argparse, glob, time, cv2, Queue, caffe

# Some Helper Functins 
def getCutout(image, x1, y1, x2, y2, border):
    assert(x1 >= 0 and y1 >= 0)
    assert(x2 > x1 and y2 >y1)
    assert(border >= 0)
    return cv2.getRectSubPix(image, (y2-y1 + 2*border, x2-x1 + 2*border), (((y2-1)+y1) / 2.0, ((x2-1)+x1) / 2.0))

def fillRndData(data, net):
    inputLayer = 'data'
    randomChannels = net.blobs[inputLayer].data.shape[1]
    rndData = np.random.randn(data.shape[0], randomChannels, data.shape[2], data.shape[3]).astype(np.float32) * 0.2
    rndData[:,0:1,:,:] = data
    net.blobs[inputLayer].data[...] = rndData[:,0:1,:,:]

def mkdirp(directory):
    if not os.path.isdir(directory):
        os.makedirs(directory)

The main function start here

def main(argv):
    pycaffe_dir = os.path.dirname(__file__)

    parser = argparse.ArgumentParser()
    # Optional arguments.
    parser.add_argument(
        "--model_def",
        help="Model definition file.",
        required=True
    )
    parser.add_argument(
        "--pretrained_model",
        help="Trained model weights file.",
        required=True
    )
    parser.add_argument(
        "--out_scale",
        help="Scale of the output image.",
        default=1.0,
        type=float
    )
    parser.add_argument(
        "--output_path",
        help="Output path.",
        default=''
    )
    parser.add_argument(
        "--tile_resolution",
        help="Resolution of processing tile.",
        required=True,
        type=int
    )
    parser.add_argument(
        "--suffix",
        help="Suffix of the output file.",
        default="-deblur",
    )
    parser.add_argument(
        "--gpu",
        action='store_true',
        help="Switch for gpu computation."
    )
    parser.add_argument(
        "--grey_mean",
        action='store_true',
        help="Use grey mean RGB=127. Default is the VGG mean."
    )
    parser.add_argument(
        "--use_mean",
        action='store_true',
        help="Use mean."
    )
    parser.add_argument(
        "--adversarial",
        action='store_true',
        help="Use mean."
    )
    args = parser.parse_args()

    mkdirp(args.output_path)

    if hasattr(caffe, 'set_mode_gpu'):
        if args.gpu:
            print('GPU mode', file=sys.stderr)
            caffe.set_mode_gpu()
        net = caffe.Net(args.model_def, args.pretrained_model, caffe.TEST)
    else:
        if args.gpu:
            print('GPU mode', file=sys.stderr)
        net = caffe.Net(args.model_def, args.pretrained_model, gpu=args.gpu)


    inputs = [line.strip() for line in sys.stdin]

    print("Classifying %d inputs." % len(inputs), file=sys.stderr)


    inputBlob = net.blobs.keys()[0] # [innat]: input shape 
    outputBlob = net.blobs.keys()[-1]

    print( inputBlob, outputBlob)
    channelCount = net.blobs[inputBlob].data.shape[1]
    net.blobs[inputBlob].reshape(1, channelCount, args.tile_resolution, args.tile_resolution)
    net.reshape()

    if channelCount == 1 or channelCount > 3:
        color = 0
    else:
        color = 1

    outResolution = net.blobs[outputBlob].data.shape[2]
    inResolution = int(outResolution / args.out_scale)
    boundary = (net.blobs[inputBlob].data.shape[2] - inResolution) / 2

    for fileName in inputs:
        img = cv2.imread(fileName, flags=color).astype(np.float32)
        original = np.copy(img)
        img = img.reshape(img.shape[0], img.shape[1], -1)
        if args.use_mean:
            if args.grey_mean or channelCount == 1:
                img -= 127
            else:
                img[:,:,0] -= 103.939
                img[:,:,1] -= 116.779
                img[:,:,2] -= 123.68
        img *= 0.004

        outShape = [int(img.shape[0] * args.out_scale) ,
                    int(img.shape[1] * args.out_scale) ,
                    net.blobs[outputBlob].channels]
        imgOut = np.zeros(outShape)

        imageStartTime = time.time()
        for x, xOut in zip(range(0, img.shape[0], inResolution), range(0, imgOut.shape[0], outResolution)):
            for y, yOut in zip(range(0, img.shape[1], inResolution), range(0, imgOut.shape[1], outResolution)):

                start = time.time()

                region = getCutout(img, x, y, x+inResolution, y+inResolution, boundary)
                region = region.reshape(region.shape[0], region.shape[1], -1)
                data = region.transpose([2, 0, 1]).reshape(1, -1, region.shape[0], region.shape[1])

                if args.adversarial:
                    fillRndData(data, net)
                    out = net.forward()
                else:
                    out = net.forward_all(data=data)

                out = out[outputBlob].reshape(out[outputBlob].shape[1], out[outputBlob].shape[2], out[outputBlob].shape[3]).transpose(1, 2, 0)

                if imgOut.shape[2] == 3 or imgOut.shape[2] == 1:
                    out /= 0.004
                    if args.use_mean:
                        if args.grey_mean:
                            out += 127
                        else:
                            out[:,:,0] += 103.939
                            out[:,:,1] += 116.779
                            out[:,:,2] += 123.68

                if out.shape[0] != outResolution:
                    print("Warning: size of net output is %d px and it is expected to be %d px" % (out.shape[0], outResolution))
                if out.shape[0] < outResolution:
                    print("Error: size of net output is %d px and it is expected to be %d px" % (out.shape[0], outResolution))
                    exit()

                xRange = min((outResolution, imgOut.shape[0] - xOut))
                yRange = min((outResolution, imgOut.shape[1] - yOut))

                imgOut[xOut:xOut+xRange, yOut:yOut+yRange, :] = out[0:xRange, 0:yRange, :]
                imgOut[xOut:xOut+xRange, yOut:yOut+yRange, :] = out[0:xRange, 0:yRange, :]

                print(".", end="", file=sys.stderr)
                sys.stdout.flush()


        print(imgOut.min(), imgOut.max())
        print("IMAGE DONE %s" % (time.time() - imageStartTime))
        basename = os.path.basename(fileName)
        name = os.path.join(args.output_path, basename + args.suffix)
        print(name, imgOut.shape)
        cv2.imwrite( name, imgOut)

if __name__ == '__main__':
    main(sys.argv)

To run the program:

cat fileListToProcess.txt | python processWholeImage.py --model_def ./BMVC_nets/S14_19_200.deploy --pretrained_model ./BMVC_nets/S14_19_FQ_178000.model --output_path ./out/ --tile_resolution 300 --suffix _out.png --gpu --use_mean

The weight files and also the above scripts can be download from here (BMVC_net). However, you may want to convert caffe2pytorch. In order to do that, here is the basic starting point:

  • install proto-lens
  • clone caffemodel2pytorch

Next,

# BMVC_net, you need to download it from authors website, link above
model = caffemodel2pytorch.Net(
    prototxt = './BMVC_net/S14_19_200.deploy', 
    weights = './BMVC_net/S14_19_FQ_178000.model',
    caffe_proto = 'https://raw.githubusercontent.com/BVLC/caffe/master/src/caffe/proto/caffe.proto'
)

model.cuda()
model.eval()
torch.set_grad_enabled(False)

Run-on a demo tensor,

# make sure to have right procedure of image normalization and channel reordering
image = torch.Tensor(8, 3, 98, 98).cuda()

# outputs dict of PyTorch Variables
# in this example the dict contains the only key "prob"
#output_dict = model(data = image)

# you can remove unneeded layers:
#del model.prob
#del model.fc8

# a single input variable is interpreted as an input blob named "data"
# in this example the dict contains the only key "fc7"
output_dict = model(image)
# print(output_dict)
print(output_dict.keys())

Please note, there are some basic things to consider; the networks expect text at DPI 120-150, reasonable orientation, and reasonable black and white levels. The networks expect to mean [103.9, 116.8, 123.7] to be subtracted from inputs. The inputs should be further multiplied by 0.004.

🌐
Analytics Vidhya
analyticsvidhya.com › home › top 8 ocr libraries in python to extract text from image
Top 8 OCR Libraries in Python to Extract Text from Image
April 16, 2025 - Let’s take a peek into python OCR image to text libraries in Python and see how these libraries turn images into readable text! Understand what optical character recognition (OCR) is and its applications · Explore the top 8 OCR libraries in Python: EasyOCR, Doctr, Keras-OCR, Tesseract, GOCR, Pytesseract, OpenCV, and Amazon Textract · Learn how to install and implement each OCR library in Python ... EasyOCR simplifies text extraction from images in Python with its user-friendly approach and deep learning-powered model.
🌐
Width.ai
width.ai › post › the-best-ways-to-extract-text-from-images-without-tesseract-python
The Best Ways To Extract Text From Images Without Tesseract (Python) | Width.ai
March 15, 2022 - Manually observe patterns in the layouts, edges, colors, textures, font sizes, contours, morphologies (shapes), and any other features that help differentiate text areas from non-text regions. Use preprocessing operations — like converting image format, thresholding, erosion, dilation, color masking, colorspace conversions, or gaussian blurring — to isolate the text regions we're interested in.
🌐
GitHub
github.com › meijianhan › DeepDeblur
GitHub - meijianhan/DeepDeblur: DeepDeblur: Text Image Recovery from Blur to Sharp · GitHub
This work focuses on recovering the blurry text image. Based on the deep neural network, a new short connection scheme is used. Trained by the pixel regression, higher visual quality of the image can be recovered by the network from the blurry one.
Starred by 140 users
Forked by 24 users
Languages   Python 81.6% | MATLAB 18.4%