forked from cvdlab/react-planner
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Modified server.js with new API call, logics to run python script; mo…
…dified toolbar fetch button to call the new API call; and added python script to server folder
- Loading branch information
1 parent
7586fe3
commit d94f525
Showing
6 changed files
with
293 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
import cv2 | ||
import os | ||
import numpy as np | ||
import pyautogui | ||
import time | ||
from selenium import webdriver | ||
from selenium.webdriver import Firefox, FirefoxOptions | ||
from selenium.webdriver.common.keys import Keys | ||
from selenium.webdriver import ActionChains | ||
from selenium.webdriver.common.by import By | ||
|
||
# Get the current directory | ||
current_dir = os.path.dirname(os.path.abspath(__file__)) | ||
|
||
# Image file name | ||
image_file = "uploads/floor_plan.png" # Include the 'uploads' directory in the path | ||
output_file = "uploads/output.png" # Include the 'uploads' directory in the path | ||
|
||
# Image file path | ||
image_path = os.path.join(current_dir, image_file) | ||
output_path = os.path.join(current_dir, output_file) | ||
|
||
# Check if the image file exists | ||
if not os.path.isfile(image_path): | ||
raise FileNotFoundError(f"Image file '{image_file}' not found.") | ||
|
||
def getWalls(image_path, threshold_area, canny_threshold1, canny_threshold2): | ||
# Load the image | ||
image = cv2.imread(image_path) | ||
|
||
# Convert the image to grayscale | ||
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | ||
|
||
# Apply thresholding to segment the walls | ||
_, thresholded = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) | ||
|
||
# Apply Canny edge detection to detect lines | ||
edges = cv2.Canny(thresholded, canny_threshold1, canny_threshold2) | ||
|
||
# Remove small components or noise from the detected lines | ||
num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(edges, connectivity=8) | ||
|
||
# Create a mask to filter out small components | ||
mask = np.zeros_like(labels, dtype=np.uint8) | ||
for label in range(1, num_labels): | ||
area = stats[label, cv2.CC_STAT_AREA] | ||
if area > threshold_area: | ||
mask[labels == label] = 255 | ||
|
||
# Apply the mask to retain only the lines along the walls | ||
result = cv2.bitwise_and(edges, edges, mask=mask) | ||
|
||
# Perform morphological dilation to connect adjacent line segments | ||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15)) | ||
dilated = cv2.dilate(result, kernel, iterations=1) | ||
|
||
# Display the resulting image [UNDO TO SEE MID RESULT] | ||
## cv2.imshow('Original Image', image) | ||
## cv2.imshow('Lines along Walls', dilated) | ||
cv2.imwrite(output_path, dilated) | ||
|
||
cv2.waitKey(0) | ||
cv2.destroyAllWindows() | ||
|
||
def reduction(pathToOutput): | ||
img = cv2.imread(pathToOutput, cv2.IMREAD_GRAYSCALE) | ||
|
||
# Set the desired thickness reduction factor | ||
thickness_reduction_factor = 9 | ||
|
||
# Define the structuring element for erosion | ||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) | ||
|
||
# Perform erosion | ||
eroded_image = cv2.erode(img, kernel, iterations=thickness_reduction_factor) | ||
|
||
# Display the eroded image [UNDO TO SEE MID RESULT] | ||
## cv2.imshow("Eroded Image", eroded_image) | ||
cv2.waitKey(0) | ||
cv2.destroyAllWindows() | ||
cv2.imwrite(output_path, eroded_image) | ||
|
||
def detect_line_segments(image_path): | ||
# Load the image in grayscale | ||
image = cv2.imread(image_path, 0) | ||
|
||
# Apply Canny edge detection | ||
edges = cv2.Canny(image, 50, 150, apertureSize=3) | ||
|
||
# Perform Hough Line Transform | ||
lines = cv2.HoughLinesP(edges, rho=1, theta=np.pi / 180, threshold=100, minLineLength=20, maxLineGap=10) | ||
|
||
# Process the detected lines | ||
dimensions = [] | ||
if lines is not None: | ||
for line in lines: | ||
x1, y1, x2, y2 = line[0] | ||
length = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) | ||
angle = np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi | ||
dimensions.append((x1, y1, x2, y2, length, angle)) | ||
|
||
return dimensions | ||
|
||
# Call the function to reduce the image to lines and label them | ||
threshold_area = 90 | ||
canny_threshold1 = 10 | ||
canny_threshold2 = 10 | ||
thickness_reduction_factor = 1 | ||
|
||
# Call the function to remove background elements and retain lines along walls | ||
getWalls(image_path, threshold_area, canny_threshold1, canny_threshold2) | ||
|
||
reduction(output_path) | ||
|
||
line_segments = detect_line_segments(output_path) | ||
|
||
list_of_xcor = [] | ||
list_of_ycor = [] | ||
|
||
for segment in line_segments: | ||
x1, y1, x2, y2, length, angle = segment | ||
print(f"Start Point: ({x1}, {y1}), End Point: ({x2}, {y2}), Length: {length}, Angle: {angle}") | ||
list_of_xcor.append(x1) | ||
list_of_ycor.append(y1) | ||
list_of_xcor.append(x2) | ||
list_of_ycor.append(y2) | ||
|
||
list_of_xcor = [int(x) for x in list_of_xcor] | ||
list_of_ycor = [int(y) for y in list_of_ycor] | ||
|
||
# Set the downloads directory path | ||
downloads_dir = os.path.join(current_dir, "jsons") | ||
if not os.path.exists(downloads_dir): | ||
os.makedirs(downloads_dir) | ||
|
||
# Set up Firefox options | ||
opts = FirefoxOptions() | ||
opts.add_argument("--width=4000") | ||
opts.add_argument("--height=4000") | ||
opts.add_argument('--headless') | ||
|
||
# Set Firefox Preferences to specify the custom download directory | ||
opts.set_preference("browser.download.folderList", 2) # Use custom download path | ||
opts.set_preference("browser.download.manager.showWhenStarting", False) | ||
opts.set_preference("browser.download.dir", downloads_dir) | ||
opts.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream") # MIME type | ||
|
||
# Create the driver with the custom options | ||
driver = Firefox(options=opts) | ||
driver.get('https://cvdlab.github.io/react-planner/') | ||
actionChains = ActionChains(driver) | ||
|
||
def selectWall(i): | ||
cssSelector_OpenMenu = ".toolbar > div:nth-child(4) > div:nth-child(1) > svg:nth-child(1)" | ||
## cssSelector = "#app > div:nth-child(1) > div:nth-child(2) > div:nth-child(4) > svg:nth-child(1) > g:nth-child(2) > g:nth-child(2) > g:nth-child(2) > g:nth-child(1) > rect:nth-child(1)" | ||
|
||
button = driver.find_element(By.CSS_SELECTOR, cssSelector_OpenMenu) | ||
actionChains.move_to_element(button).click().perform() | ||
time.sleep(0.20) | ||
if i != 0: | ||
cssSelector_Wall = "#app > div:nth-child(1) > div:nth-child(2) > div:nth-child(4) > div:nth-child(3)" | ||
else: | ||
cssSelector_Wall = "#app > div:nth-child(1) > div:nth-child(2) > div:nth-child(3) > div:nth-child(3)" | ||
button = driver.find_element(By.CSS_SELECTOR, cssSelector_Wall) | ||
actionChains.move_to_element(button).click().perform() | ||
|
||
|
||
def drawWall(x1, y1, x2, y2): | ||
## CSS Properties of the Grid | ||
cssSelector_Grid = "#app > div:nth-child(1) > div:nth-child(2) > div:nth-child(4) > svg:nth-child(1) > g:nth-child(2) > g:nth-child(2) > g:nth-child(2) > g:nth-child(1) > rect:nth-child(1)" | ||
|
||
## Grid Size = (3000, 2000) | ||
## Center = (1500,1000) | ||
Offset_X1 = x1 - 1500 | ||
Offset_Y1 = y1 - 1000 | ||
|
||
Offset_X2 = x2 - 1500 | ||
Offset_Y2 = y2 - 1000 | ||
|
||
button = driver.find_element(By.CSS_SELECTOR, cssSelector_Grid) | ||
actionChains.move_to_element_with_offset(button, Offset_X1, Offset_Y1) | ||
actionChains.click() | ||
actionChains.move_to_element_with_offset(button, Offset_X2, Offset_Y2) | ||
actionChains.click() | ||
actionChains.send_keys(Keys.ESCAPE).perform() | ||
|
||
coords = list(map(list, zip(list_of_xcor, list_of_ycor))) | ||
|
||
for i in range(0,len(coords),2): | ||
selectWall(i) | ||
time.sleep(0.20) | ||
drawWall(coords[i][0], coords[i][1], coords[i+1][0], coords[i+1][1]) | ||
|
||
def saveProject(): | ||
time.sleep(0.20) | ||
## Properties of the Save Button | ||
cssSelector_Save = ".toolbar > div:nth-child(2) > div:nth-child(1) > svg:nth-child(1) > path:nth-child(1)" | ||
## Find the Save Button | ||
button = driver.find_element(By.CSS_SELECTOR, cssSelector_Save) | ||
## Move to and click the save Button | ||
actionChains.move_to_element(button).click().perform() | ||
|
||
## Wait for it to load properly in case | ||
time.sleep(0.20) | ||
## Accept the alert pop-up | ||
driver.switch_to.alert.accept() | ||
|
||
saveProject() | ||
time.sleep(1) | ||
print("clicked") | ||
driver.quit() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,88 @@ | ||
const express = require('express'); | ||
const cors = require('cors'); | ||
const multer = require('multer'); | ||
const fs = require('fs'); | ||
const express = require("express"); | ||
const cors = require("cors"); | ||
const multer = require("multer"); | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
const port = 4000; | ||
const { exec } = require("child_process"); | ||
|
||
// Ensure the uploads directory exists | ||
const uploadsDir = './uploads'; | ||
fs.existsSync(uploadsDir) || fs.mkdirSync(uploadsDir); | ||
// Initialize express app | ||
const app = express(); | ||
|
||
// Enable CORS with predefined options | ||
app.use( | ||
cors({ | ||
origin: ["http://localhost:9000", "http://localhost:44463"], // Frontend domains | ||
optionsSuccessStatus: 200, | ||
}) | ||
); | ||
|
||
// Set storage engine for multer | ||
const storage = multer.diskStorage({ | ||
destination: function (req, file, cb) { | ||
cb(null, uploadsDir) // Save to the 'uploads' directory | ||
}, | ||
destination: "./uploads/", | ||
filename: function (req, file, cb) { | ||
cb(null, file.originalname) | ||
} | ||
// Set a fixed file name | ||
const newFilename = "floor_plan.png"; | ||
cb(null, newFilename); | ||
}, | ||
}); | ||
|
||
const fileFilter = (req, file, cb) => { | ||
// Accept JSON files only | ||
if (file.mimetype === 'application/json' || file.originalname.endsWith('.json')) { | ||
// Initialize upload variable with multer configuration | ||
const upload = multer({ | ||
storage: storage, | ||
// File filter for PNG files | ||
fileFilter: function (req, file, cb) { | ||
checkFileType(file, cb); | ||
}, | ||
}).single("file"); // 'file' is the name attribute in your form | ||
|
||
// Check file type to ensure it's a PNG | ||
function checkFileType(file, cb) { | ||
if (file.mimetype === "image/png") { | ||
cb(null, true); | ||
} else { | ||
cb(new Error('Not a JSON file'), false); | ||
cb("Error: Only PNG files are allowed!"); | ||
} | ||
}; | ||
|
||
const upload = multer({ storage: storage, fileFilter: fileFilter }); | ||
|
||
const app = express(); | ||
app.use(cors({ | ||
origin: ['http://localhost:9000', 'http://localhost:44463'], // Frontend domains | ||
optionsSuccessStatus: 200 | ||
})); | ||
|
||
app.post('/upload-json', upload.single('file'), (req, res) => { | ||
if (req.file) { | ||
fs.readFile(req.file.path, 'utf8', (err, data) => { | ||
if (err) { | ||
console.error('Error reading the file:', err); | ||
return res.status(500).json({ message: 'Error reading the file' }); | ||
} | ||
|
||
// Assuming the file content is a JSON object | ||
const jsonData = JSON.parse(data); | ||
console.log(jsonData); // Print the JSON content to the server console | ||
|
||
// Save the JSON data to a single project data file | ||
fs.writeFile('./uploads/project-data.json', data, (writeErr) => { | ||
if (writeErr) { | ||
console.error('Error writing project data:', writeErr); | ||
return res.status(500).json({ message: 'Error writing project data' }); | ||
} | ||
} | ||
|
||
// Delete the uploaded file after saving project data | ||
fs.unlinkSync(req.file.path); | ||
// Serve static files from the 'uploads' directory | ||
app.use("/uploads", express.static("uploads")); | ||
|
||
res.json({ message: 'JSON data received', data: jsonData }); | ||
// Route for uploading PNG files | ||
app.post("/upload-png", (req, res) => { | ||
upload(req, res, (err) => { | ||
if (err) { | ||
res.send({ | ||
message: err, | ||
}); | ||
}); | ||
} else { | ||
res.status(400).json({ message: 'No file received or the file is not a JSON file' }); | ||
} | ||
} else { | ||
if (req.file == undefined) { | ||
res.send({ | ||
message: "Error: No File Selected!", | ||
}); | ||
} else { | ||
res.send({ | ||
message: "File Uploaded!", | ||
fileInfo: { | ||
filename: req.file.filename, | ||
path: req.file.path, | ||
}, | ||
}); | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
app.get('/get-project-data', (req, res) => { | ||
// Read the project data from the project data file | ||
fs.readFile('./uploads/project-data.json', 'utf8', (err, data) => { | ||
if (err) { | ||
console.error('Error reading project data:', err); | ||
return res.status(500).json({ message: 'Error reading project data' }); | ||
// Route to execute a Python script and return its output | ||
app.get("/process-projects", (req, res) => { | ||
exec("python floor_plan.py", (error, stdout, stderr) => { | ||
if (error) { | ||
console.error(`exec error: ${error}`); | ||
return res.status(500).send("Failed to execute Python script."); | ||
} | ||
|
||
const jsonData = JSON.parse(data); | ||
res.json(jsonData); | ||
res.send(stdout); | ||
}); | ||
}); | ||
|
||
const PORT = 4000; | ||
app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); | ||
// Start the server on the specified port | ||
app.listen(port, () => console.log(`Server started on port ${port}`)); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters