В настоящее время я работаю над проектом OCR, в котором мне нужно прочитать текст из счета-фактуры (см. образец изображения ниже). У меня проблемы с перекосом изображения, а также с форматом вывода, который я получаю. Мне нужна помощь в исправлении перекоса изображения, чтобы текст был горизонтальным, а не наклонен так же, как вывод, я хочу, чтобы выходной формат был таким же, как оригинальный документ. Сначала я попробовал вспомогательный формат поля, чтобы напечатать текст в поле, чтобы структура была такой же, но поскольку файл наклонен, формат создает новые проблемы, когда я извлекаю его с помощью fuzzywuzzy. В настоящее время мне нужны ресурсы или предложения для решения моей проблемы.
код:
# Class FieldReader with fuzzy matching and flexible prefix matching
class FieldReader:
def __init__(self, field_name, keywords, prefix=None, default="-", threshold=60):
self.field_name = field_name
self.keywords = keywords
self.prefix = prefix
self.default = default
self.threshold = threshold
def extract(self, text):
words = text.split()
potential_value = None
for keyword in self.keywords:
# Use fuzzy matching to find the best match for the keyword in the text
best_match = process.extractOne(keyword.rstrip(':'), words, scorer=fuzz.partial_ratio)
if best_match and best_match[1] >= self.threshold:
index = words.index(best_match[0])
# Search for the ":" in the following words
for i in range(index + 1, len(words)):
if ":" in words[i]:
# If found, remove anything between keyword and ":" (clean unwanted characters)
if i > index + 1:
words[index + 1:i] = [] # Remove intermediate words between keyword and ":"
# Extract the word immediately following the ":"
if i + 1 < len(words):
potential_value = words[i + 1]
else:
potential_value = None
break
# If a prefix is defined, check if the value starts with that prefix (without specifying length)
if self.prefix and potential_value and potential_value.startswith(self.prefix):
return potential_value # Return the value if it starts with the prefix
elif potential_value:
return potential_value # Return the value if no prefix is specified or matches
return self.default # Return default value if no match is found
class FieldReader_regex:
def __init__(self, field_name, pattern):
self.field_name = field_name
self.pattern = pattern
def extract(self, text):
# Mencari semua kecocokan dengan regex
matches = re.findall(self.pattern, text)
# Mengambil hanya hasil pertama atau mengembalikan '-' jika tidak ada
return matches[0] if matches else "-"
# Function to extract information from text using fuzzy matching
def extract_information(text, provider):
data = {}
if provider == "Pertamina":
patterns = [
FieldReader("Date", ["Date", "Tanggal", "Issue Date"], default="-"),
FieldReader_regex("Aircraft Reg", r"\bPK\w*\b"), # Ambil semua yang berawalan PK untuk Aircraft Reg
FieldReader_regex("Flight Numbers", r"\bQZ\w*\b"), # Ambil semua yang berawalan QZ untuk nomor penerbangan
# FieldReader("Reg", ["Reg", "Reg g", "Aircraft Reg","Aircraft Reg g", "Alrcraft Reg", "AIRCRAFT REG"], prefix="PK", default="-"),
# FieldReader("Flight No", ["Flight Number", "Flight No", "Flight Nunber","Flight N0", "Fllght Number", "FLIGHT NUMBER"], prefix ="QZ", default="-"),
FieldReader("Dep", ["IATA", "Departure", "Dep", "TATA", "lATA"], default="-"),
FieldReader("Uplift_in_Lts", ["Quantity", "QUANTITY", "Fuel Quantity", "Uplift", "Quantity", "Delivered Volume (L)"], default="-"),
FieldReader_regex("Invoice", r"DELIVERY RECEIPT\s*(\S+)"),
FieldReader("Provider", ["Provider", "Supplier"], default="Pertamina")
]
elif provider == "Shell":
patterns = [
FieldReader("Date", ["Date", "Issue Date"], default="-"),
FieldReader("Reg", ["Reg", "Aircraft Reg"], default="-"),
FieldReader("Flight No", ["Flight No"], default="-"),
FieldReader("Dep", ["IATA", "Departure"], default="-"),
FieldReader("Arr", ["Next Dest", "Arrival"], default="-"),
FieldReader("Uplift_in_Lts", ["QUANTITY", "Fuel Quantity"], default="-"),
FieldReader_regex("Aircraft Reg", r"\bPK\w*\b"), # Ambil semua yang berawalan PK untuk Aircraft Reg
FieldReader_regex("Invoice", r"DELIVERY RECEIPT\s*(\S+)"),
FieldReader("Provider", ["Provider", "Supplier"], default="SHELL")
]
for field in patterns:
data[field.field_name] = field.extract(text)
return data
# Function to preprocess the image (grayscale and threshold)
def preprocess_image(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
return thresh
# Function to draw boxes and extract text based on boxes with left-to-right order
def extract_text_by_boxes(result, image_width, margin=2):
box_data = []
box_counter = 1
for line in result:
for word_info in line:
box = word_info[0] # Coordinates of the box
text, conf = word_info[1] # Text and confidence
# Get coordinates of the word box
x_min, y_min = box[0]
x_max, y_max = box[2]
# Adjust box to cover the entire width of the image (left to right)
extended_box = [[0, y_min - margin], [image_width, y_min - margin], [image_width, y_max + margin], [0, y_max + margin]]
box_data.append((box_counter, extended_box, text))
box_counter += 1
return box_data
# Function to perform OCR based on bounding boxes
def ocr_boxes(processed_image, box_data):
full_text = ""
for box_info in box_data:
box_counter, extended_box, _ = box_info
# Create a mask for the bounding box
mask = np.zeros_like(processed_image)
cv2.fillPoly(mask, [np.array(extended_box).astype(np.int32)], (255,))
# Extract the region of interest (ROI) using the mask
roi = cv2.bitwise_and(processed_image, mask)
# OCR the ROI without rotation
ocr = PaddleOCR(use_angle_cls=True, lang='en')
result = ocr.ocr(roi, cls=True)
# Concatenate the extracted text
for line in result:
if not line: # This checks for None or empty list
continue
for word_info in line:
text = word_info[1][0]
full_text += f" {text}"
# Draw the box for visualization
cv2.polylines(processed_image, [np.array(extended_box).astype(np.int32)], isClosed=True, color=(0, 255, 0), thickness=2)
cv2.putText(processed_image, str(box_counter), (int(extended_box[0][0]), int(extended_box[0][1] - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
return full_text
# Function to extract text from images with boxes and sort by box order
def getText(file_path):
images = convert_from_path(file_path, dpi=300)
full_text_all = ""
for i, page in enumerate(images):
page_array = np.array(page)
page_array = cv2.cvtColor(page_array, cv2.COLOR_RGB2BGR)
# Apply preprocessing (optional)
processed_image = preprocess_image(page_array)
image_width = processed_image.shape[1]
# OCR the processed image
ocr = PaddleOCR(use_angle_cls=True, lang='en')
result = ocr.ocr(processed_image, cls=True)
# Extract text using custom boxes
box_data = extract_text_by_boxes(result, image_width, margin=0)
# Sort box data by box order
box_data.sort(key=lambda x: x[0])
# Perform OCR on each bounding box
full_text = ocr_boxes(processed_image, box_data)
full_text_all += full_text
# Print the extracted text from the current image (for debugging)
print(f"\nExtracted text from image {i + 1}:\n{full_text}")
# Display the processed image with boxes
cv2_imshow(processed_image)
# Save the processed image (optional)
cv2.imwrite(f"processed_page_{i+1}.png", processed_image)
return full_text_all
пример:
введите здесь описание изображения
вывод был напечатан неправильно и не соответствует.
Если кто-нибудь может мне помочь разберитесь в этой проблеме, это очень поможет.
В настоящее время я работаю над проектом OCR, в котором мне нужно прочитать текст из счета-фактуры (см. образец изображения ниже). У меня проблемы с перекосом изображения, а также с форматом вывода, который я получаю. Мне нужна помощь в исправлении перекоса изображения, чтобы текст был горизонтальным, а не наклонен так же, как вывод, я хочу, чтобы выходной формат был таким же, как оригинальный документ. Сначала я попробовал вспомогательный формат поля, чтобы напечатать текст в поле, чтобы структура была такой же, но поскольку файл наклонен, формат создает новые проблемы, когда я извлекаю его с помощью fuzzywuzzy. В настоящее время мне нужны ресурсы или предложения для решения моей проблемы. код: [code]# Class FieldReader with fuzzy matching and flexible prefix matching class FieldReader: def __init__(self, field_name, keywords, prefix=None, default="-", threshold=60): self.field_name = field_name self.keywords = keywords self.prefix = prefix self.default = default self.threshold = threshold
def extract(self, text): words = text.split() potential_value = None
for keyword in self.keywords: # Use fuzzy matching to find the best match for the keyword in the text best_match = process.extractOne(keyword.rstrip(':'), words, scorer=fuzz.partial_ratio) if best_match and best_match[1] >= self.threshold: index = words.index(best_match[0])
# Search for the ":" in the following words for i in range(index + 1, len(words)): if ":" in words[i]: # If found, remove anything between keyword and ":" (clean unwanted characters) if i > index + 1: words[index + 1:i] = [] # Remove intermediate words between keyword and ":"
# Extract the word immediately following the ":" if i + 1 < len(words): potential_value = words[i + 1] else: potential_value = None break
# If a prefix is defined, check if the value starts with that prefix (without specifying length) if self.prefix and potential_value and potential_value.startswith(self.prefix): return potential_value # Return the value if it starts with the prefix
elif potential_value: return potential_value # Return the value if no prefix is specified or matches
return self.default # Return default value if no match is found
def extract(self, text): # Mencari semua kecocokan dengan regex matches = re.findall(self.pattern, text) # Mengambil hanya hasil pertama atau mengembalikan '-' jika tidak ada return matches[0] if matches else "-"
# Function to extract information from text using fuzzy matching def extract_information(text, provider): data = {} if provider == "Pertamina": patterns = [ FieldReader("Date", ["Date", "Tanggal", "Issue Date"], default="-"), FieldReader_regex("Aircraft Reg", r"\bPK\w*\b"), # Ambil semua yang berawalan PK untuk Aircraft Reg FieldReader_regex("Flight Numbers", r"\bQZ\w*\b"), # Ambil semua yang berawalan QZ untuk nomor penerbangan # FieldReader("Reg", ["Reg", "Reg g", "Aircraft Reg","Aircraft Reg g", "Alrcraft Reg", "AIRCRAFT REG"], prefix="PK", default="-"), # FieldReader("Flight No", ["Flight Number", "Flight No", "Flight Nunber","Flight N0", "Fllght Number", "FLIGHT NUMBER"], prefix ="QZ", default="-"), FieldReader("Dep", ["IATA", "Departure", "Dep", "TATA", "lATA"], default="-"), FieldReader("Uplift_in_Lts", ["Quantity", "QUANTITY", "Fuel Quantity", "Uplift", "Quantity", "Delivered Volume (L)"], default="-"), FieldReader_regex("Invoice", r"DELIVERY RECEIPT\s*(\S+)"), FieldReader("Provider", ["Provider", "Supplier"], default="Pertamina") ] elif provider == "Shell": patterns = [ FieldReader("Date", ["Date", "Issue Date"], default="-"), FieldReader("Reg", ["Reg", "Aircraft Reg"], default="-"), FieldReader("Flight No", ["Flight No"], default="-"), FieldReader("Dep", ["IATA", "Departure"], default="-"), FieldReader("Arr", ["Next Dest", "Arrival"], default="-"), FieldReader("Uplift_in_Lts", ["QUANTITY", "Fuel Quantity"], default="-"), FieldReader_regex("Aircraft Reg", r"\bPK\w*\b"), # Ambil semua yang berawalan PK untuk Aircraft Reg FieldReader_regex("Invoice", r"DELIVERY RECEIPT\s*(\S+)"), FieldReader("Provider", ["Provider", "Supplier"], default="SHELL") ]
for field in patterns: data[field.field_name] = field.extract(text)
return data
# Function to preprocess the image (grayscale and threshold) def preprocess_image(image): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY) return thresh
# Function to draw boxes and extract text based on boxes with left-to-right order def extract_text_by_boxes(result, image_width, margin=2): box_data = [] box_counter = 1
for line in result: for word_info in line: box = word_info[0] # Coordinates of the box text, conf = word_info[1] # Text and confidence
# Get coordinates of the word box x_min, y_min = box[0] x_max, y_max = box[2]
# Adjust box to cover the entire width of the image (left to right) extended_box = [[0, y_min - margin], [image_width, y_min - margin], [image_width, y_max + margin], [0, y_max + margin]] box_data.append((box_counter, extended_box, text)) box_counter += 1
return box_data
# Function to perform OCR based on bounding boxes def ocr_boxes(processed_image, box_data): full_text = "" for box_info in box_data: box_counter, extended_box, _ = box_info
# Create a mask for the bounding box mask = np.zeros_like(processed_image) cv2.fillPoly(mask, [np.array(extended_box).astype(np.int32)], (255,))
# Extract the region of interest (ROI) using the mask roi = cv2.bitwise_and(processed_image, mask)
# OCR the ROI without rotation ocr = PaddleOCR(use_angle_cls=True, lang='en') result = ocr.ocr(roi, cls=True)
# Concatenate the extracted text for line in result: if not line: # This checks for None or empty list continue for word_info in line: text = word_info[1][0] full_text += f" {text}"
# Function to extract text from images with boxes and sort by box order def getText(file_path): images = convert_from_path(file_path, dpi=300) full_text_all = ""
for i, page in enumerate(images): page_array = np.array(page) page_array = cv2.cvtColor(page_array, cv2.COLOR_RGB2BGR)
# OCR the processed image ocr = PaddleOCR(use_angle_cls=True, lang='en') result = ocr.ocr(processed_image, cls=True)
# Extract text using custom boxes box_data = extract_text_by_boxes(result, image_width, margin=0)
# Sort box data by box order box_data.sort(key=lambda x: x[0])
# Perform OCR on each bounding box full_text = ocr_boxes(processed_image, box_data) full_text_all += full_text
# Print the extracted text from the current image (for debugging) print(f"\nExtracted text from image {i + 1}:\n{full_text}")
# Display the processed image with boxes cv2_imshow(processed_image)
# Save the processed image (optional) cv2.imwrite(f"processed_page_{i+1}.png", processed_image)
return full_text_all [/code] пример: введите здесь описание изображения вывод был напечатан неправильно и не соответствует. Если кто-нибудь может мне помочь разберитесь в этой проблеме, это очень поможет.
Я хочу опробовать разные модели в PaddleOCR, поэтому делаю это:
from paddleocr import PaddleOCR
ocr = PaddleOCR(det_model_name='en_PP-OCRv3_det', rec_model_name='en_number_mobile_v2.0_rec', lang='en')
Я хочу опробовать разные модели в PaddleOCR, поэтому делаю это:
from paddleocr import PaddleOCR
ocr = PaddleOCR(det_model_name='en_PP-OCRv3_det', rec_model_name='en_number_mobile_v2.0_rec', lang='en')
На самом деле я пытаюсь использовать «paddleocr» с Python
Я использую среду conda
Я следовал руководству на их github:
Но когда я пытаюсь выполнить эту команду: (paddle_env) C:\Windows\system32>paddleocr --image_dir path\image --use_angle_cls true...
Я прочитал бесчисленное количество статей и официальной документации, объясняющих, как точно настроить Paddle, но постоянно сталкиваюсь с проблемами. Я считаю, что документация также расплывчата. Модель по умолчанию хорошо подходит для моего...