# Project export: A Cropping Drought

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: CruzHacks 2024
- Tagline: Save money and the world with this website! Give us your info, see the prices, and reimagine your lawn to be drought-safe.
- Devpost: https://devpost.com/software/a-cropping-drought
- GitHub: https://github.com/Solbjor/Sustain-Lawn
- Demo: https://github.com/BustosAndrew/lawn-frontend/blob/main/src/routes/results.js
- Team: 3 GitHub contributor(s) — Andrew (6 commits), CodingEmpire (4 commits), mr008 (2 commits)

## Devpost submission (written by the team)

### Inspiration

Lots of water is used on front lawns annually. We wanted to try and help consumer make any easier choice by seeing their options that will save them money

### What it does

This website converts thirsty lawns into fake ones.

### How we built it

We built it by creating our own AI.

### Challenges we ran into

The biggest challenge we ran into was not having any data or api for determining lawns so we had to create our own data.

### Accomplishments we're proud of

This was two of our team member's first time using AI to solve a problem and they had a lot of fun.

### What we learned

We learned a lot. We had to understand how to clean our data before training our model.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 6 recognized source files, 13 KB.
- Python (language) — detected in the code
- Django (technology) — claimed on Devpost, not found in the code
- Firebase (technology) — claimed on Devpost, not found in the code
- JavaScript (language) — claimed on Devpost, not found in the code
- Tailwind CSS (technology) — claimed on Devpost, not found in the code
- TensorFlow (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (20 of 20)

```
.vscode/settings.json
ACroppingAI.h5
aiSustain.py
aiTrainer.py
data.json
fingerprint.pb
imagereader.py
keras_metadata.pb
prepjson.py
saved_model.pb
TeamsAI.h5
train/newdata.json
train/traindata.json
trainingai.py
turfstyles.py
variables/variables.data-00000-of-00001
variables/variables.index
via_project_20Jan2024_15h35m_json.json
X_train.npy
y_train.npy
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Final design
- newtraindata
- Updated code
- New data added
- added train.json
- changed stuff
- Add files via upload
- Add files via upload
- for you
- fix image reader
- Add files via upload
- Add files via upload

## Key source files (fetched from GitHub, selected and truncated for size)

### aiSustain.py

```python
import cv2
import numpy as np
import tensorflow as tf

# Load the trained model
model_path = '' 
model = tf.saved_model.load(model_path)

def detect_lawn(image):
    # Preprocess the image
    input_tensor = tf.convert_to_tensor(np.expand_dims(image, 0), dtype=tf.uint8)
    detections = model(input_tensor)

    # Extract bounding box coordinates
    boxes = detections['detection_boxes'].numpy()[0]
    height, width, _ = image.shape

    # Draw bounding boxes on the image
    for box in boxes:
        ymin, xmin, ymax, xmax = box
        ymin, xmin, ymax, xmax = int(ymin * height), int(xmin * width), int(ymax * height), int(xmax * width)
        cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)

    return image

# Read image data from CSV file
csv_file_path = 'C:\Users\s0lbj\OneDrive\Desktop\sustainApp\images_in_csv\via_project_20Jan2024_11h52m_csv.csv'
image_data = np.genfromtxt(csv_file_path, delimiter=',')

# Reshape image data if necessary
image_shape = (100, 100, 2)  # Replace with the dimensions
image_data = image_data.reshape(image_shape)

# Perform object detection
image_with_boxes = detect_lawn(image_data.astype(np.uint8))

# Display the result
cv2.imshow('Lawn Detection', image_with_boxes)
cv2.waitKey(0)
cv2.destroyAllWindows()

```

### imagereader.py

```python
import os
import json
import cv2
import tensorflow as tf

# Path to the JSON file containing annotations
json_file_path = r'C:\Users\s0lbj\OneDrive\Desktop\sustainApp/test.json'

# Directory containing images
image_dir = r'C:\Users\s0lbj\OneDrive\Desktop\homeImages'

# Check if the JSON file exists
if not os.path.exists(json_file_path):
    print("JSON file not found:", json_file_path)
else:
    print("JSON file found. Reading data...")

    # Load annotations from JSON file
    with open(json_file_path, 'r') as json_file:
        annotations = json.load(json_file)

    print("JSON data loaded successfully.")

# Prepare your data structure for TensorFlow
data_for_tf = []

for key, item in annotations.items():
    filename = item['filename']
    image_path = os.path.join(image_dir, filename)
    image = cv2.imread(image_path)

    # Check if the image is loaded properly
    if image is not None:
        # Assuming 'regions' is a list of dictionaries containing 'shape_attributes'
        for region in item['regions']:
            points_x = region['shape_attributes']['all_points_x']
            points_y = region['shape_attributes']['all_points_y']

            for i in range(len(points_x)):
                start_point = (points_x[i], points_y[i])
                end_point = (points_x[(i + 1) % len(points_x)], points_y[(i + 1) % len(points_y)])
                cv2.line(image, start_point, end_point, (0, 255, 0), 2)

        cv2.imshow('Annotated Image', image)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    else:
        print(f"Failed to load image: {image_path}")
```

### trainingai.py

```python
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf

# Load the trained model
model = tf.keras.models.load_model(r'C:\Users\codyk\OneDrive\Desktop\Sustain-Lawn\TeamsAI.h5')

# Function to preprocess the image
def preprocess_image(image_path, desired_size=(260, 260)):
    if not os.path.exists(image_path):
        print(f"File not found: {image_path}")
        return None
    image = cv2.imread(image_path)
    if image is None:
        print(f"Failed to load image: {image_path}")
        return None
    image = cv2.resize(image, desired_size)
    image = image / 255.0  # Normalize
    return image

# Function to postprocess the mask and draw an outline
def postprocess_and_draw_outline(image, mask):
    # Convert mask to binary
    _, binary_mask = cv2.threshold(mask, 0.5, 1, cv2.THRESH_BINARY)
    
    # Find contours
    contours, _ = cv2.findContours(np.uint8(binary_mask), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # Draw an outline around the detected regions
    outline_color = (0, 255, 0)  # Green color for outline
    outline_thickness = 1
    for contour in contours:
        cv2.drawContours(image, [contour], 0, outline_color, outline_thickness)
    
    return image

# Path to the new image
new_image_path = r'C:\Users\codyk\OneDrive\Desktop\well-tended_lawns_part_of_american_dream.jpg'

# Preprocess the image
new_image = preprocess_image(new_image_path)

if new_image is not None:
    # Predict the mask
    predicted_mask = model.predict(np.array([new_image]))[0]

    # Postprocess the prediction and draw an outline
    final_image = postprocess_and_draw_outline(new_image, predicted_mask[:,:,0])

    # Display the image
    plt.imshow(final_image)
    plt.show()
else:
    print("Image processing failed.")

```

### turfstyles.py

```python
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf


# Load the trained model
model = tf.keras.models.load_model(r'C:\Users\codyk\OneDrive\Desktop\Sustain-Lawn\ACroppingAI.h5')


def preprocess_image(image_path, desired_size=(300, 300)):
    if not os.path.exists(image_path):
        print(f"File not found: {image_path}")
        return None
    image = cv2.imread(image_path)
    if image is None:
        print(f"Failed to load image: {image_path}")
        return None
    #image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)  # Convert to RGB
    image = cv2.resize(image, desired_size)
    image = image / 255.0  # Normalize
    return image


def postprocess_and_fill_lawn(image, mask):
    _, binary_mask = cv2.threshold(mask, 0.5, 1, cv2.THRESH_BINARY)
    lawn_mask = np.zeros_like(image)
    contours, _ = cv2.findContours(np.uint8(binary_mask), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    fill_color = (255, 255, 255)
    for contour in contours:
        cv2.fillPoly(lawn_mask, [contour], fill_color)
    return lawn_mask


def adjust_brightness_contrast(image, alpha=1.0, beta=0):
    adjusted_image = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
    return adjusted_image


def overlay_images_with_mask(background, overlay, lawn_mask, alpha=0.7):
    background = background.astype(np.uint8)
    overlay = overlay.astype(np.uint8)
    overlay_blend = cv2.addWeighted(background, 1 - alpha, overlay, alpha, 0)
    return np.where(lawn_mask == 1, overlay_blend, background)


new_image_path = r'C:\Users\codyk\OneDrive\Desktop\realtest.jpg'
overlay_image_path = r'C:\Users\codyk\OneDrive\Desktop\turf.jpg'
new_image = preprocess_image(new_image_path)


if new_image is not None:
    # Apply brightness and contrast adjustment
    new_image_adjusted = adjust_brightness_contrast(new_image * 255, alpha=0.8, beta=30)  # Modify alpha and beta as needed
    

    predicted_mask = model.predict(np.array([new_image_adjusted]))[0]
    lawn_mask = postprocess_and_fill_lawn(new_image_adjusted, predicted_mask[:,:,0])
    lawn_mask = (lawn_mask > 0).astype(np.uint8)


    overlay_image = cv2.imread(overlay_image_path)
    overlay_image = cv2.resize(overlay_image, (lawn_mask.shape[1], lawn_mask.shape[0]))
    final_image = overlay_images_with_mask(new_image_adjusted, overlay_image, lawn_mask)

    final_image_rgb = cv2.cvtColor(final_image, cv2.COLOR_BGR2RGB)
    plt.imshow(final_image_rgb)
    plt.show()

else:
    print("Image processing failed.")

```

### prepjson.py

```python
import os
import json
import cv2
import numpy as np
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# Paths
json_file_path1 = r'C:\Users\codyk\OneDrive\Desktop\Sustain-Lawn\train\newdata.json'
json_file_path2 = r'C:\Users\codyk\OneDrive\Desktop\Sustain-Lawn\train\traindata.json'
image_dir = r'C:\Users\codyk\OneDrive\Desktop\homeImages'

# Load annotations
with open(json_file_path1, 'r') as file:
    data1 = json.load(file)
    annotations1 = data1 if '_via_img_metadata' not in data1 else data1['_via_img_metadata']

with open(json_file_path2, 'r') as file:
    annotations2 = json.load(file)['_via_img_metadata']

# Combine annotations
annotations = {**annotations1, **annotations2} 

# Prepare dataset
images = []
masks = []

desired_size = (300, 300)  # Define the desired size (width, height)

for key, value in annotations.items():
    filename = value['filename']
    image_path = os.path.join(image_dir, filename)
    image = cv2.imread(image_path, cv2.IMREAD_COLOR)

    if image is not None:
        # Resize image to the desired size
        resized_image = cv2.resize(image, desired_size)
        images.append(resized_image)

        # Create a blank mask with the desired size
        mask = np.zeros(desired_size, dtype=np.uint8)
        for region in value['regions']:
            # Scale the annotation points to the new size
            scaled_points_x = [int(x * desired_size[0] / image.shape[1]) for x in region['shape_attributes']['all_points_x']]
            scaled_points_y = [int(y * desired_size[1] / image.shape[0]) for y in region['shape_attributes']['all_points_y']]
            points = np.array(list(zip(scaled_points_x, scaled_points_y)), dtype=np.int32)
            cv2.fillPoly(mask, [points], 1)
        masks.append(mask)
    else:
        print(f"Failed to load image: {image_path}")

# Convert lists to NumPy arrays
images = np.array(images)
masks = np.array(masks)

# Save the NumPy arrays as files
np.save('X_train.npy', images)
np.save('y_train.npy', masks)

# Split dataset
X_train, X_val, y_train, y_val = train_test_split(images, masks, test_size=0.2, random_state=42)

# Print a few images for visualization
num_images_to_display = 1  # You can adjust this based on how many images you want to display

for i in range(num_images_to_display):
    plt.figure(figsize=(8, 8))
   
    # Convert BGR to RGB for display
    image_rgb = cv2.cvtColor(X_train[i], cv2.COLOR_BGR2RGB)

    plt.subplot(1, 2, 1)
    plt.imshow(image_rgb)
    plt.title(f'Training Image {i+1}')
   
    plt.subplot(1, 2, 2)
    plt.imshow(y_train[i], cmap='gray')
    plt.title(f'Training Mask {i+1}')

    plt.show()

# Now, you can proceed with the rest of your code
print("Arrays saved successfully!")
print("Total images in training set:", len(X_train))
print("Total masks in training set:", len(y_train))

```

### aiTrainer.py

```python
import os
import json
import cv2
import numpy as np
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D, concatenate

# Paths
json_file_path1 = r'C:\Users\codyk\OneDrive\Desktop\Sustain-Lawn\train\newdata.json'
json_file_path2 = r'C:\Users\codyk\OneDrive\Desktop\Sustain-Lawn\train\traindata.json'
image_dir = r'C:\Users\codyk\OneDrive\Desktop\homeImages'

# Load annotations
with open(json_file_path1, 'r') as file:
    data1 = json.load(file)
    annotations1 = data1 if '_via_img_metadata' not in data1 else data1['_via_img_metadata']

with open(json_file_path2, 'r') as file:
    annotations2 = json.load(file)['_via_img_metadata']

# Combine annotations
annotations = {**annotations1, **annotations2} 

# Prepare dataset
images = []
masks = []

desired_size = (300, 300)  # Define the desired size (width, height)

for key, value in annotations.items():
    filename = value['filename']
    image_path = os.path.join(image_dir, filename)
    image = cv2.imread(image_path, cv2.IMREAD_COLOR)

    if image is not None:
        # Resize image to the desired size
        resized_image = cv2.resize(image, desired_size)
        images.append(resized_image)

        # Create a blank mask with the desired size
        mask = np.zeros(desired_size, dtype=np.uint8)
        for region in value['regions']:
            # Scale the annotation points to the new size
            scaled_points_x = [int(x * desired_size[0] / image.shape[1]) for x in region['shape_attributes']['all_points_x']]
            scaled_points_y = [int(y * desired_size[1] / image.shape[0]) for y in region['shape_attributes']['all_points_y']]
            points = np.array(list(zip(scaled_points_x, scaled_points_y)), dtype=np.int32)
            cv2.fillPoly(mask, [points], 1)
        masks.append(mask)
    else:
        print(f"Failed to load image: {image_path}")

# Convert lists to NumPy arrays
images = np.array(images)
masks = np.array(masks)

# Split dataset
X_train, X_val, y_train, y_val = train_test_split(images, masks, test_size=0.2, random_state=42)

# Now, reshape data for model training
X_train_reshaped = X_train.reshape(-1, 300, 300, 3)
y_train_reshaped = y_train.reshape(-1, 300, 300, 1)
X_val_reshaped = X_val.reshape(-1, 300, 300, 3)
y_val_reshaped = y_val.reshape(-1, 300, 300, 1)

# Define the U-Net model
def unet_model(input_size=(300, 300, 3)):
    inputs = Input(input_size)
    
    # Down-sampling
    conv1 = Conv2D(64, 3, activation='relu', padding='same')(inputs)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)

    # More down-sampling layers can be added here

    # Up-sampling
    up1 = UpSampling2D(size=(2, 2))(pool1)
    conv2 = Conv2D(64, 2, activation='relu', padding='same')(up1)
    merged = concatenate([conv1, conv2], axis=3)
    conv3 = Conv2D(64, 3, activation='relu', padding='same')(merged)

    # Output layer
    conv4 = Conv2D(1, 1, activation='sigmoid')(conv3)

    model = Model(inputs=inputs, outputs=conv4)

    return model

# Instantiate and compile the model
model = unet_model()
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# Train the model
model.fit(X_train_reshaped, y_train_reshaped, validation_data=(X_val_reshaped, y_val_reshaped), epochs=10, batch_size=32)

model.save('ACroppingAI.h5')

print("Done!")

```