Skip to content

Commit

Permalink
black background mask file generated, needs to change to transparent …
Browse files Browse the repository at this point in the history
…mask with specific colors and adjust the file name.
  • Loading branch information
cnpcshangbo committed Jul 24, 2024
1 parent 8e6ba95 commit 278a1e6
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 88 deletions.
2 changes: 1 addition & 1 deletion NodeODM
Submodule NodeODM updated 1 files
+791 −678 libs/Task.js
13 changes: 8 additions & 5 deletions configure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,18 @@ installNodeODM() {
ln -s /code/SuperBuild/install/bin/pdal /usr/bin/pdal

# Create installation directory
sudo mkdir -p /var/www/NodeODM
sudo chown -R "$USER":"$USER" /var/www/NodeODM
cd /var/www/NodeODM
cd /code
git config --global --add safe.directory /code
git submodule add https://github.com/CCNYRoboticsLab/NodeODM.git
# sudo mkdir -p /var/www/NodeODM
sudo chown -R "$USER":"$USER" /code/NodeODM
cd /code/NodeODM

# Clone NodeODM repository
git clone https://github.com/OpenDroneMap/NodeODM.git
# git clone https://github.com/OpenDroneMap/NodeODM.git

# Install Node.js dependencies
cd NodeODM
# cd NodeODM
npm install --production && mkdir -p tmp

# Create symbolic link for easier access (optional)
Expand Down
14 changes: 14 additions & 0 deletions gpu.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ WORKDIR /code
# Copy everything
COPY . ./

# Install necessary tools
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
gnupg

# Add Kitware GPG key
RUN curl -sSL https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor -o /usr/share/keyrings/kitware-archive-keyring.gpg

# Add Kitware repository
RUN echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu focal main" >> /etc/apt/sources.list.d/kitware.list

# Update package lists
RUN apt-get update

# Run the build
RUN PORTABLE_INSTALL=YES GPU_INSTALL=YES bash configure.sh install

Expand Down
97 changes: 54 additions & 43 deletions opendm/staindetection.py
Original file line number Diff line number Diff line change
@@ -1,78 +1,89 @@
import cv2
import numpy as np
from opendm import log
from matplotlib import cm
import onnxruntime as ort


class StainDetector: # Renamed the class
class StainDetector:
def __init__(self, model_path):
self.sess = ort.InferenceSession(model_path)
self.input_name = self.sess.get_inputs()[0].name
self.output_name = self.sess.get_outputs()[0].name

input_shape = self.sess.get_inputs()[0].shape
self.input_height, self.input_width = input_shape[2:]

def detect_and_overlay(self, input_image, output_image):
def detect_and_overlay(self, input_image, output_image, mask_output=None, alpha=0.5):
"""
Detect stains in the input image, create a transparent overlay, and optionally save the mask.
Args:
input_image (str): Path to the input image file.
output_image (str): Path to save the output overlay image.
mask_output (str, optional): Path to save the mask image. If None, mask is not saved.
alpha (float): Transparency level for the overlay (0.0 to 1.0).
"""
image = cv2.imread(input_image)
if image is None:
raise ValueError(f"Could not load image: {input_image}")

preprocessed_image = self.preprocess(image)
detections = self.detect_stains(preprocessed_image)
overlay = self.create_overlay(image, detections)
original_height, original_width = image.shape[:2]
preprocessed_image = self._preprocess(image)
detections = self._detect_stains(preprocessed_image, original_height, original_width)

if mask_output:
self._save_mask(detections, mask_output)

overlay = self._create_overlay(image, detections, alpha)
cv2.imwrite(output_image, overlay)

def preprocess(self, image):
def _preprocess(self, image):
"""Preprocess the input image for the ONNX model."""
img = cv2.resize(image, (self.input_width, self.input_height))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = img.astype(np.float32) / 255.0
img = img.transpose(2, 0, 1) # Transpose to CHW format
img = img.reshape(1, *img.shape)
return img

def detect_stains(self, preprocessed_image):
def _detect_stains(self, preprocessed_image, original_height, original_width):
"""Detect stains using the ONNX model and resize the mask to the original image size."""
try:
input_data = {self.input_name: preprocessed_image}
out = self.sess.run([self.output_name], input_data)[0]

segm_mask = np.argmax(out, axis=1)
segm_mask = (segm_mask * 255 / segm_mask.max()).astype(np.uint8)
segm_mask = np.squeeze(segm_mask, axis=0)

return segm_mask

# Resize the mask to match the original image dimensions
resized_mask = cv2.resize(segm_mask, (original_width, original_height), interpolation=cv2.INTER_NEAREST)

return resized_mask
except Exception as e:
log.ODM_ERROR(f"Error during crack detection: {str(e)}")
return []

def create_overlay(self, original_image, segm_mask):
original_height, original_width = original_image.shape[:2]
resized_segm_mask = cv2.resize(
segm_mask,
(original_width, original_height),
interpolation=cv2.INTER_NEAREST,
)

# # Create a red color mask for stains
# stain_mask = np.zeros_like(original_image)
# stain_mask[resized_segm_mask > 0] = [0, 0, 255] # Set red color for stains
log.ODM_ERROR(f"Error during stain detection: {str(e)}")
return np.zeros((original_height, original_width), dtype=np.uint8)

# # Combine the original image with the stain mask using alpha blending
# alpha = 0.5 # Transparency level (0.5 for 50% transparency)
# overlay_image = cv2.addWeighted(
# original_image, 1 - alpha, stain_mask, alpha, 0
# )

def _create_overlay(self, original_image, segm_mask, alpha=0.5):
"""Create a transparent overlay of the original image with detected stains."""
# Create a red color mask for stains
stain_mask = np.zeros_like(original_image)
stain_mask[resized_segm_mask > 0] = [0, 0, 255] # Set red color for stains

# Directly modify the original image based on the mask
original_image[resized_segm_mask > 0] = stain_mask[resized_segm_mask > 0]

# cv2.imwrite("output_onnx_overlay.jpg", original_image)



return original_image
stain_mask[segm_mask > 0] = [0, 0, 255] # Set red color for stains

# Create the overlay using alpha blending
overlay = cv2.addWeighted(original_image, 1, stain_mask, alpha, 0)

return overlay

def _save_mask(self, mask, output_path):
"""Save the segmentation mask as an image file."""
cv2.imwrite(output_path, mask)

# Example usage
if __name__ == "__main__":
model_path = "path/to/your/model.onnx"
detector = StainDetector(model_path)

input_image = "path/to/input/image.jpg"
output_image = "path/to/output/overlay.jpg"
mask_output = "path/to/output/mask.jpg"

# You can adjust the alpha value (0.0 to 1.0) to control the transparency
detector.detect_and_overlay(input_image, output_image, mask_output, alpha=0.5)
2 changes: 1 addition & 1 deletion stages/crack_segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def find_mask(photo_path, masks):

# Load crack detection model
model = ai.get_model(
"crackdetection", "http://localhost:44289/config.json", "v1.0.0"
"crackdetection", "http://192.168.13.108:44289/config.json", "v1.0.0"
)
if model is None:
log.ODM_WARNING(
Expand Down
57 changes: 19 additions & 38 deletions stages/stain_segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,14 @@
from opendm import system
import os

# from opendm.concurrency import parallel_map

# from stages.dataset import valid_filename
# from stages.dataset import find_mask, get_images
from opendm import context
from opendm.photo import PhotoCorruptedException
from concurrent.futures import ThreadPoolExecutor, as_completed


class ODMStainSegmentationStage(types.ODM_Stage):
def process(self, args, outputs):
log.ODM_INFO("Running stain detection on images")

# reconstruction = outputs["reconstruction"]
# photos = reconstruction.photos
outputs["start_time"] = system.now_raw()
tree = types.ODM_Tree(args.project_path, args.gcp, args.geo, args.align)
outputs["tree"] = tree
Expand All @@ -43,7 +36,6 @@ def valid_filename(filename, supported_extensions):
(pathfn, ext) = os.path.splitext(filename)
return ext.lower() in supported_extensions and pathfn[-5:] != "_mask"

# Get supported images from dir
def get_images(in_dir):
entries = os.listdir(in_dir)
valid, rejects = [], []
Expand All @@ -54,21 +46,12 @@ def get_images(in_dir):
rejects.append(f)
return valid, rejects

def search_video_files(in_dir):
entries = os.listdir(in_dir)
return [
os.path.join(in_dir, f)
for f in entries
if valid_filename(f, context.supported_video_extensions)
]

def find_mask(photo_path, masks):
(pathfn, ext) = os.path.splitext(os.path.basename(photo_path))
k = "{}_mask".format(pathfn)

mask = masks.get(k)
if mask:
# Spaces are not supported due to OpenSfM's mask_list.txt format reqs
if not " " in mask:
return mask
else:
Expand All @@ -80,15 +63,17 @@ def find_mask(photo_path, masks):

images_dir = outputs["tree"].dataset_raw
stain_overlay_dir = outputs["tree"].stain_overlay
if not os.path.exists(stain_overlay_dir):
system.mkdir_p(stain_overlay_dir)
stain_mask_dir = os.path.join(outputs["tree"].root_path, "stain_masks") # New directory for stain masks

# Create directories if they don't exist
for dir_path in [stain_overlay_dir, stain_mask_dir]:
if not os.path.exists(dir_path):
system.mkdir_p(dir_path)

files, rejects = get_images(images_dir)
if files:
# create ODMPhoto list
path_files = [os.path.join(images_dir, f) for f in files]

# Lookup table for masks
masks = {}
for r in rejects:
(p, ext) = os.path.splitext(r)
Expand All @@ -110,7 +95,6 @@ def find_mask(photo_path, masks):
% os.path.basename(f)
)

# Load stain detection model
model = ai.get_model(
"staindetection", "http://192.168.13.108:44289/config.json", "v1.0.0"
)
Expand All @@ -125,24 +109,20 @@ def find_mask(photo_path, masks):

def process_image(photo):
input_image = os.path.join(images_dir, photo.filename)
output_image = os.path.join(stain_overlay_dir, photo.filename)
output_overlay = os.path.join(stain_overlay_dir, photo.filename)
output_mask = os.path.join(stain_mask_dir, os.path.splitext(photo.filename)[0] + "_mask.png")

try:
stain_detector.detect_and_overlay(input_image, output_image)
log.ODM_INFO(f"Generated stain overlay for {photo.filename}")
geo_copier.process_image(input_image, output_image)
return output_image
stain_detector.detect_and_overlay(input_image, output_overlay, output_mask)
log.ODM_INFO(f"Generated stain overlay and mask for {photo.filename}")
geo_copier.process_image(input_image, output_overlay)
return output_overlay, output_mask
except Exception as e:
log.ODM_WARNING(
f"Failed to generate stain overlay for {photo.filename}: {str(e)}"
f"Failed to generate stain overlay and mask for {photo.filename}: {str(e)}"
)
# return None
raise

# overlay_images = parallel_map(
# process_image, photos, max_workers=args.max_concurrency
# )

def parallel_map(func, iterable, max_workers=None):
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(func, item): item for item in iterable}
Expand All @@ -152,14 +132,15 @@ def parallel_map(func, iterable, max_workers=None):
except Exception as e:
log.ODM_ERROR(f"Error processing {futures[future]}: {e}")

overlay_images = list(
results = list(
parallel_map(process_image, photos, max_workers=args.max_concurrency)
)

# Filter out None values (failed detections)
overlay_images = [img for img in overlay_images if img is not None]
overlay_images = [result[0] for result in results if result is not None]
mask_images = [result[1] for result in results if result is not None]

outputs["stain_overlay_images"] = overlay_images
outputs["stain_mask_images"] = mask_images
log.ODM_INFO(
f"Completed stain detection. Generated {len(overlay_images)} overlay images."
)
f"Completed stain detection. Generated {len(overlay_images)} overlay images and {len(mask_images)} mask images."
)

0 comments on commit 278a1e6

Please sign in to comment.