Optical character recognition (OCR) converts text visible in images or scanned documents into machine-readable characters. For a digital twin, it can recover historical readings from utility bills and current readings from photographs of analog meters. Recognition is only part of the job: the resulting text must still be parsed into useful values, checked, and stored with its date or other context. section 2.3 section 2.3.1 section 2.3.2
Build a consumption baseline from bills
A scanned gas bill contains a current meter reading and its date. A Python workflow opens the bill image, passes it to Tesseract through pytesseract, then uses regular expressions to find the reading and a date. Once the extraction rules fit the bill format, the workflow can process archived bills to assemble a 10-year consumption baseline. That baseline exposes seasonal patterns and supplies historical context for later prediction; OCR errors still require attention before the data is trusted. section 2.3.1
Listing 2.1 Extracting the meter reading by using OCR to digitize data
from PIL import Image
import pytesseract, re
def extract_meter_reading(text):
pattern = r"Current Meter Reading:\s*(\d+)" #1
match = re.search(pattern, text)
return int(match.group(1)) if match else None
def extract_date(text):
pattern = r"(\d{1,2}/\d{1,2}/\d{4})" #2
matches = re.findall(pattern, text)
return matches[-1] if matches else None
def read_bill():
image = Image.open(r"./images/gasBill.png")
text = pytesseract.image_to_string(image)
meter_reading = extract_meter_reading(text)
date = extract_date(text)
return {
"meter_reading": meter_reading,
"date": date
}
The listing separates character recognition from parsing: Tesseract returns text, while regular expressions select the meter reading and date. section 2.3.1
A meter photograph needs more preparation than a scanned bill. Crop the image to the digit area, convert it to grayscale, invert it, and ask Tesseract to recognize a single line of digits. The example extracts 42678 from a water-meter photo; its timestamp and location can accompany the value in the digital twin. A fixed camera can repeat this process more often than quarterly bills arrive. section 2.3.2
Listing 2.2 Example code that extracts a reading of an analog meter using OCR
import cv2, pytesseract
import numpy as np
from PIL import Image, ImageOps
def extract_meter_reading(image_path, display_results=True):
img = Image.open(image_path)
crop_box = (1000, 890, 1700, 1030) #1
cropped_img = img.crop(crop_box) #2
gray_img = ImageOps.grayscale(cropped_img) #3
inverted_img = ImageOps.invert(gray_img) #4
meter_reading = pytesseract.image_to_string( #5
inverted_img,
config='--psm 7 -c tessedit_char_whitelist=0123456789'
)
meter_reading = meter_reading.strip() #6
return meter_reading
The listing shows the crop and image transformations before OCR, including a digit whitelist. Its crop coordinates fit the example image rather than every meter. section 2.3.2
For a remote, battery-powered meter camera, sending every image to the cloud may be impractical. An edge device can extract the reading locally and transmit the value instead: one deployment reduced a 100 KB image to a 10-byte reading over LoRaWAN. Periodic raw images can help verify results. Field accuracy remains an operational task—shadows, glare, and condensation required calibration images, model retraining, and a meter enclosure. section 3.4.1

