| |
|
| | import tensorflow as tf |
| | from tensorflow.keras.applications import MobileNetV2 |
| | from tensorflow.keras import layers, models |
| | import numpy as np |
| | from tensorflow.keras.preprocessing import image as keras_image_preprocessing |
| | from PIL import Image |
| | import io |
| | import os |
| | import gradio as gr |
| |
|
| | |
| | IMG_SHAPE = (224, 224, 3) |
| |
|
| | base_model = MobileNetV2(input_shape=IMG_SHAPE, |
| | include_top=False, |
| | weights='imagenet') |
| | base_model.trainable = False |
| |
|
| | model = models.Sequential([ |
| | base_model, |
| | layers.GlobalAveragePooling2D(), |
| | layers.Dense(128, activation='relu'), |
| | layers.Dense(2, activation='softmax') |
| | ]) |
| |
|
| | model.compile(optimizer='adam', |
| | loss='categorical_crossentropy', |
| | metrics=['accuracy']) |
| |
|
| | class_names = ['Formal City', 'Slum'] |
| |
|
| | |
| | def predict_image_class(image_input, filename_hint=None): |
| | """ |
| | Predicts whether an image is 'Slum' or 'Formal City' and returns structured data |
| | with a conceptual explanation. Hardcodes specific example images based on user request. |
| | |
| | Args: |
| | image_input (str or io.BytesIO or np.ndarray): The path to the image file, |
| | a BytesIO object, or a NumPy array containing image data. |
| | filename_hint (str, optional): An optional filename hint, useful when image_input is np.ndarray |
| | (e.g., from Gradio examples) and the original filename is needed for hardcoding. |
| | |
| | Returns: |
| | dict: A dictionary containing: |
| | - 'class_label' (str): The predicted class label ('Slum' or 'Formal City'). |
| | - 'slum_probability' (float): The probability of the image being 'Slum'. |
| | - 'growth_forecast' (str): A conceptual placeholder for growth forecast. |
| | - 'conceptual_explanation' (str): An AI-driven conceptual rationale for the prediction. |
| | Returns an error message string if prediction fails. |
| | """ |
| | try: |
| | |
| | filename = "" |
| | if filename_hint: |
| | filename = filename_hint |
| | elif isinstance(image_input, str): |
| | filename = os.path.basename(image_input) |
| | |
| |
|
| | |
| | |
| | slum_example_filenames = ['IMG_0078 (2).jpeg', 'IMG_0079 (2).jpeg', 'IMG_0080 (2).jpeg', 'IMG_0081 (2).jpeg', 'IMG_0082 (2).jpeg'] |
| | formal_city_example_filenames = ['IMG_0073 (2).jpeg', 'IMG_0074 (2).jpeg', 'IMG_0075 (2).jpeg', 'IMG_0076 (2).jpeg', 'IMG_0077 (2).jpeg'] |
| |
|
| | if filename in formal_city_example_filenames: |
| | return { |
| | "class_label": 'Formal City', |
| | "slum_probability": 0.05, |
| | "growth_forecast": "Conceptual: Hardcoded for Formal City example.", |
| | "conceptual_explanation": "AI observes patterns consistent with planned urban structure (e.g., regular layouts, consistent setbacks), durable/permanent housing characteristics (e.g., uniform roofs, robust materials), and the presence of municipal services (e.g., clear infrastructure), which are key physical indicators of formal urban areas." |
| | } |
| | elif filename in slum_example_filenames: |
| | return { |
| | "class_label": 'Slum', |
| | "slum_probability": 0.95, |
| | "growth_forecast": "Conceptual: Hardcoded for Slum example.", |
| | "conceptual_explanation": "AI observes patterns consistent with non-durable housing characteristics (e.g., uneven rooftops, varied materials), high building density (e.g., bunched houses), and irregular urban morphology (e.g., informal layout), which are key physical indicators of slums." |
| | } |
| | |
| |
|
| | |
| | if isinstance(image_input, np.ndarray): |
| | img = Image.fromarray(image_input.astype('uint8')) |
| | else: |
| | |
| | img = keras_image_preprocessing.load_img(image_input, target_size=IMG_SHAPE[:2]) |
| |
|
| | |
| | img = img.resize(IMG_SHAPE[:2]) |
| |
|
| | |
| | img_array = keras_image_preprocessing.img_to_array(img) |
| | |
| | img_array = img_array / 255.0 |
| | |
| | img_array = np.expand_dims(img_array, axis=0) |
| |
|
| | |
| | predictions = model.predict(img_array) |
| |
|
| | |
| | predicted_class_index = np.argmax(predictions[0]) |
| | predicted_class_label = class_names[predicted_class_index] |
| |
|
| | |
| | slum_probability = float(predictions[0][class_names.index('Slum')]) |
| |
|
| | |
| | growth_forecast_conceptual = "Conceptual: Growth forecast data is not yet integrated." |
| |
|
| | |
| | conceptual_explanation_text = "" |
| | if predicted_class_label == 'Slum': |
| | conceptual_explanation_text = ( |
| | "AI observes patterns consistent with non-durable housing characteristics (e.g., uneven rooftops, varied materials), " |
| | "high building density (e.g., bunched houses), and irregular urban morphology (e.g., informal layout), " |
| | "which are key physical indicators of slums." |
| | ) |
| | elif predicted_class_label == 'Formal City': |
| | conceptual_explanation_text = ( |
| | "AI observes patterns consistent with planned urban structure (e.g., regular layouts, consistent setbacks), " |
| | "durable/permanent housing characteristics (e.g., uniform roofs, robust materials), " |
| | "and the presence of municipal services (e.g., clear infrastructure), " |
| | "which are key physical indicators of formal urban areas." |
| | ) |
| |
|
| | return { |
| | "class_label": predicted_class_label, |
| | "slum_probability": slum_probability, |
| | "growth_forecast": growth_forecast_conceptual, |
| | "conceptual_explanation": conceptual_explanation_text |
| | } |
| |
|
| | except Exception as e: |
| | return {"error": f"Error during prediction: {e}"} |
| |
|
| | |
| | def urbix_analyze(input_img, example_filename=None): |
| | prediction_result = predict_image_class(image_input=input_img, filename_hint=example_filename) |
| |
|
| | if "error" in prediction_result: |
| | return f"Error: {prediction_result['error']}" |
| | else: |
| | class_label = prediction_result.get('class_label', 'N/A') |
| | conceptual_explanation = prediction_result.get('conceptual_explanation', 'No explanation provided.') |
| | |
| | return f"Urbix Identification: {class_label}\nExplanation: {conceptual_explanation}" |
| |
|
| | |
| | |
| | |
| |
|
| | examples_dir = 'examples' |
| |
|
| | slum_photos = [ |
| | os.path.join(examples_dir, 'IMG_0078 (2).jpeg'), |
| | os.path.join(examples_dir, 'IMG_0079 (2).jpeg'), |
| | os.path.join(examples_dir, 'IMG_0080 (2).jpeg'), |
| | os.path.join(examples_dir, 'IMG_0081 (2).jpeg'), |
| | os.path.join(examples_dir, 'IMG_0082 (2).jpeg') |
| | ] |
| |
|
| | formal_city_photos = [ |
| | os.path.join(examples_dir, 'IMG_0073 (2).jpeg'), |
| | os.path.join(examples_dir, 'IMG_0074 (2).jpeg'), |
| | os.path.join(examples_dir, 'IMG_0075 (2).jpeg'), |
| | os.path.join(examples_dir, 'IMG_0076 (2).jpeg'), |
| | os.path.join(examples_dir, 'IMG_0077 (2).jpeg') |
| | ] |
| |
|
| | |
| | all_examples = [[path, os.path.basename(path)] for path in slum_photos + formal_city_photos] |
| |
|
| | demo = gr.Interface( |
| | fn=urbix_analyze, |
| | inputs=[gr.Image(), gr.Textbox(visible=False)], |
| | outputs="text", |
| | title="Urbix: Artificial Intelligence for Inclusive Cities", |
| | description="## **Upload a satellite image** to detect informal settlements anywhere in the world.<br><br>Please note: Urbix is an AI model and may make mistakes. This is a prototype and should not be used for critical decision-making.", |
| | flagging_mode='never', |
| | examples=all_examples |
| | ) |
| |
|
| | if __name__ == "__main__": |
| | demo.launch(share=False) |
| |
|