LatestDuplicate_Working / Counting_Columns_2_1.py
Marthee's picture
Update Counting_Columns_2_1.py
20a8954 verified
import cv2
import numpy as np
import pandas as pd
import statistics
from statistics import mode
from PIL import Image
import io
import google_sheet_Legend
import pypdfium2 as pdfium
import fitz # PyMuPDF
import os
import random
import uuid
import math
def convert2img(data):
pdf = pdfium.PdfDocument(data)
page = pdf.get_page(0)
pil_image = page.render().to_pil()
pl1=np.array(pil_image)
img = cv2.cvtColor(pl1, cv2.COLOR_RGB2BGR)
img_cv2 = img
return img
def threshold(imgResult3):
#gaus4 = cv2.GaussianBlur(imgResult3, (3,3),9)
blur = cv2.blur(imgResult3,(7,7))
gray4 = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
outsu4 = cv2.threshold(gray4, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
return outsu4
def get_columns_info(outsu4, img):
mask_clmns = np.ones(img.shape[:2], dtype="uint8") * 255
mask_walls = np.ones(img.shape[:2], dtype="uint8") * 255
contours, hierarchy = cv2.findContours(image=outsu4, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)
p = [] #to save points of each contour
for i, cnt in enumerate(contours):
M = cv2.moments(cnt)
if M['m00'] != 0.0:
x1 = int(M['m10']/M['m00'])
y1 = int(M['m01']/M['m00'])
area = cv2.contourArea(cnt)
if area > (881.0*2):
perimeter = cv2.arcLength(cnt,True)
#print(perimeter)
cv2.drawContours(mask_walls, [cnt], -1, 0, -1)
if area < (881.0 * 2) and area > 90:
# maybe make it area < (881.0 * 1.5)
p.append((x1,y1))
#print(area)
cv2.drawContours(mask_clmns, [cnt], -1, 0, -1)
return p, mask_clmns, mask_walls
def get_text_from_pdf(input_pdf_path):
#pdf_document = fitz.open(input_pdf_path)
pdf_document = fitz.open("pdf", input_pdf_path)
results = []
for page_num in range(pdf_document.page_count):
page = pdf_document[page_num]
width, height = page.rect.width, page.rect.height # Get page dimensions
text_instances = [word for word in page.get_text("words") if word[4].startswith(("C", "c")) and len(word[4]) <= 5]
page.apply_redactions()
return text_instances
def calculate_midpoint(x1,y1,x2,y2):
xm = int((x1 + x2) / 2)
ym = int((y1 + y2) / 2)
return (xm, ym)
### Can work with images as the rotation_matrix is applied
def getTextsPoints(x, page):
point_list = []
pt_clm = {}
for h in x:
pt = calculate_midpoint(h[0],h[1],h[2],h[3])
pt = fitz.Point(pt[0], pt[1])
pt = pt * page.rotation_matrix
point_list.append(pt)
pt_clm[pt] = h[4]
return point_list, pt_clm
def distance(point1, point2):
x1, y1 = point1
x2, y2 = point2
return np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
def getNearestText(point_list, p):
nearbyy = []
selected_clm_point = [] #save the clmn for drawing cirlce on it
dis = []
txt_clmn = []
for i in range(len(p)):
nearest_point = min(point_list, key=lambda point: distance(point, p[i]))
dist = distance(nearest_point, p[i])
dis.append(dist)
#if dist < 10:
nearbyy.append(nearest_point)
selected_clm_point.append(p[i])
txt_clmn.append((nearest_point, p[i]))
return nearbyy, selected_clm_point, txt_clmn
def color_groups(txtpts_ky_vlu):
import random
unique_labels = list(set(txtpts_ky_vlu.values()))
def generate_rgb():
return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # RGB tuple
key_colors = {key: generate_rgb() for key in unique_labels} # Assign a unique RGB color to each key
return key_colors
def getColumnsTypesKeyValue(nearbyy, pt_clm):
words = []
for i in range(len(nearbyy)):
words.append(pt_clm[nearbyy[i]])
return words
def generate_legend(found_tuple):
word_freq = {}
for word in found_tuple:
if word in word_freq:
word_freq[word] += 1
else:
word_freq[word] = 1
data = word_freq
df = pd.DataFrame(data.items(), columns=['Column Type', 'Count'])
return df
def get_drawing_info(txt_clmn,txtpts_ky_vlu,key_colors):
#Search for each word in the txt_clmn to get the word associated to it
huge_list_clmn_clr_loc = []
for text_location, column_location in txt_clmn:
word = txtpts_ky_vlu[text_location]
huge_list_clmn_clr_loc.append((text_location, column_location, word, key_colors[word]))
return huge_list_clmn_clr_loc #text_location, column_location, word, color
def get_columns_info2(outsu4, img):
mask_clmns = np.ones(img.shape[:2], dtype="uint8") * 255
mask_walls = np.ones(img.shape[:2], dtype="uint8") * 255
contours, hierarchy = cv2.findContours(image=outsu4, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
p_column = [] #to save midpoints of each column
p_wall = [] #to save midpoints of each wall
wall_contour = []
all_points = []
wall_mid_and_full = {}
for i, cnt in enumerate(contours):
M = cv2.moments(cnt)
if M['m00'] != 0.0:
x1 = int(M['m10']/M['m00'])
y1 = int(M['m01']/M['m00'])
area = cv2.contourArea(cnt)
if area > (881.0):
perimeter = cv2.arcLength(cnt,True)
p_wall.append((x1,y1))
#print(perimeter)
cv2.drawContours(mask_walls, [cnt], -1, 0, -1)
wall_contour.append(cnt)
all_points.append((x1,y1))
wall_mid_and_full[(x1, y1)] = cnt
if area < (881.0 * 2) and area > 90:
# maybe make it area < (881.0 * 1.5)
all_points.append((x1,y1))
p_column.append((x1,y1))
#print(area)
cv2.drawContours(mask_clmns, [cnt], -1, 0, -1)
wall_mid_and_full[(x1, y1)] = cnt
return p_column, p_wall, mask_clmns, mask_walls, wall_contour, all_points, wall_mid_and_full
def get_all_wall_points(wall_contours):
all_contours = []
for cnt in wall_contours:
contour_points = [(int(point[0][0]), int(point[0][1])) for point in cnt]
all_contours.append(contour_points)
return all_contours
def get_text_wall_text(input_pdf_path):
#pdf_document = fitz.open(input_pdf_path)
pdf_document = fitz.open("pdf", input_pdf_path)
results = []
for page_num in range(pdf_document.page_count):
page = pdf_document[page_num]
width, height = page.rect.width, page.rect.height
text_instances = [word for word in page.get_text("words") if word[4].startswith(("w", "W")) and len(word[4]) <= 5]
page.apply_redactions()
return text_instances
def distance(p1, p2):
return math.hypot(p1[0] - p2[0], p1[1] - p2[1])
def assign_walls_to_texts(text_locations, wall_locations, threshold=55):
matched_texts = []
matched_walls = []
text_wall_pairs = []
for text in text_locations:
nearest_wall = min(wall_locations, key=lambda wall: distance(wall, text))
dist = distance(nearest_wall, text)
print(f"Text {text} -> Nearest wall {nearest_wall}, Distance: {dist:.2f}")
if dist < threshold:
matched_texts.append(text)
matched_walls.append(nearest_wall)
text_wall_pairs.append((text, nearest_wall))
return matched_texts, matched_walls, text_wall_pairs
def mainfun(plan_path, segmented_img):
#pdf_document = fitz.open(plan_path)
print("Main started")
pdf_document = fitz.open("pdf", plan_path)
#print(f"type of plan_path: {type(plan_path)}")
page = pdf_document[0]
img_cv2 = convert2img(plan_path)
rotation = page.rotation
derotationMatrix=page.derotation_matrix
nparr = np.frombuffer(segmented_img, np.uint8)
segmented_img_cv2 = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED)
outsu = threshold(segmented_img_cv2)
column_points, mask_clmns, mask_walls = get_columns_info(outsu, img_cv2)
texts_from_pdf = get_text_from_pdf(plan_path)
text_points, txtpts_ky_vlu = getTextsPoints(texts_from_pdf, page)
key_colors = color_groups(txtpts_ky_vlu)
nearby, slct_clms, txt_clmn = getNearestText(text_points, column_points)
columns_types_v = getColumnsTypesKeyValue(nearby, txtpts_ky_vlu)
legend = generate_legend(columns_types_v)
huge_list_clmn_clr_loc = get_drawing_info(txt_clmn, txtpts_ky_vlu, key_colors)
column_midpoints, wall_midpoints, mask_clmns, mask_walls, wall_contours, all_points, midpoint_full_contour= get_columns_info2(outsu, img_cv2)
wall_points = get_all_wall_points(wall_contours)
wall_text = get_text_wall_text(plan_path)
_,slct_walls, txt_wall = assign_walls_to_texts(text_points, all_points, 90)
all_walls = []
for wll in slct_walls:
all_walls.append(midpoint_full_contour[wll])
#This will be passed to Omar
selected_wall_contours = get_all_wall_points(all_walls)
print("Main Khalaset")
return {
'legend': legend.to_dict(orient='records'),
'num_columns_detected': len(column_points),
'num_texts': len(text_points),
'status': 'success'
}