Wednesday, June 11, 2025

 This is a summary of the book titled “The Startup Community Way” written by Brad Feld and Ian Hathaway and published by Wiley in 2020. Innovators and especially those that become entrepreneurs are in their endeavors for the long haul. With ubiquitous internet and technological advancements, they can now launch their digitally enabled business anywhere and can even do it in localized groups. These startup communities follow their own principles and logic withing collaborative, inclusive networks. This book helps such daring entrepreneurs to form and lead their own startup communities. Unlike traditional businesses, the paradigm of startup communities avoids hierarchies and is built on networks and relationships. They are complex systems that value openness and collaboration. A successful one is singular and irreplaceable. Relationships with other startup communities enable them to thrive. Leaders in startup communities become role models and inspire future entrepreneurs. Therefore, this guide becomes valuable to foster such an ecosystem.

1. The Principles of Startup Communities

Startup communities thrive due to widespread internet access and technological advancements, allowing entrepreneurs to launch businesses anywhere. These communities follow fundamental principles: leaders are entrepreneurs with long-term commitment, participation is inclusive and continuous, and success is strengthened through connections with other communities.

2. Networks Over Hierarchies

Unlike large institutions that operate through top-down hierarchies, startup communities' function through networks based on trust, shared resources, and collaborative creativity. These ecosystems flourish when leaders encourage openness and communication rather than rigid control.

3. Geographical Considerations

Although startup communities can form anywhere, location still matters. Successful communities benefit from access to talent, funding, corporate support, and a favorable legal and economic environment. A startup community integrates with the surrounding business network and entrepreneurial ecosystem.

4. Founding Entrepreneurs as Priority

A startup community’s success depends on prioritizing the needs of entrepreneurs. A thriving ecosystem includes multiple communities, but each startup group retains a distinct identity. The broader network benefits when entrepreneurs receive support from external entities like customers, investors, and service providers.

5. The Complexity of Startup Communities

These communities operate as complex adaptive systems—nonlinear, dynamic networks requiring collaboration and openness. They evolve constantly due to market shifts, technological advancements, and changing consumer demands. Success depends on adaptability rather than rigid strategies.

6. Entrepreneurial Success and Future Leadership

Entrepreneurial success generates wealth, experience, and knowledge that fuel future startups. However, success is not merely about increasing resources—it’s about having the right leadership and vision at the right time. Past triumphs serve as guidance, but startup communities don’t follow predictable trajectories.

7. Uniqueness and Irreplaceability

No two startup communities are identical and attempts to replicate Silicon Valley’s model are misguided. Leaders can’t control outcomes; they can only cultivate favorable conditions for success. Since startup ecosystems thrive unpredictably, adaptation and continuous improvement matter more than copying existing frameworks.

8. Interconnectivity Among Startup Communities

These communities don’t exist in isolation. Connectivity strengthens performance through shared knowledge, resources, and collaboration. Relationships with other startup networks—both local and global—help startups grow stronger.

9. Entrepreneurs as Role Models

Startup leaders must embody integrity, openness, and mentorship. Strong leadership fosters trust and motivates future entrepreneurs to build successful businesses. Leaders must eliminate negative influences while supporting individuals who drive positive change.

This book serves as a guide for entrepreneurs to understand and navigate the complexities of startup communities, ensuring long-term success through collaboration, adaptability, and network-driven leadership.


Tuesday, June 10, 2025

 The use of UAV swarm is better applied to surveying, remote sensing, disaster preparedness and responses such as wildfires, and those that make use of LiDAR data. Power line and windmill monitoring companies are especially suited for making use of a fleet of drones. Besides, there are over ten LiDAR companies that are public in US and many more across Europe and Asia that make use of a fleet of drones, photogrammetry and LiDAR data. Those that are using simultaneous localization and mapping (SLAM), structure-from-motion (SfM), and semantic segmentation with CNNs are possibly building their own knowledge bases, so it would not hurt to show them one that is built in the cloud in incremental, observable and near real-time. With GPS and satellite imagery, most terrains are navigable but vision-based navigation enables autonomous navigation and one day hopefully at all heights.

Sample SLAM to compare images:

import cv2

import numpy as np

from vslam_py import FeatureMatcher

# Load the aerial images

image1 = cv2.imread("hoover_tower_forward.jpg", cv2.IMREAD_GRAYSCALE)

image2 = cv2.imread("hoover_tower_reverse.jpg", cv2.IMREAD_GRAYSCALE)

# Initialize ORB feature detector

orb = cv2.ORB_create()

# Detect keypoints and descriptors

keypoints1, descriptors1 = orb.detectAndCompute(image1, None)

keypoints2, descriptors2 = orb.detectAndCompute(image2, None)

# Use the vSLAM-py matcher (or OpenCV's BFMatcher)

matcher = FeatureMatcher()

matches = matcher.match(descriptors1, descriptors2)

# Draw matches

output_image = cv2.drawMatches(image1, keypoints1, image2, keypoints2, matches[:50], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

# Display the results

cv2.imshow("Matched Features", output_image)

cv2.waitKey(0)

cv2.destroyAllWindows()

Emerging trends:

Constructing an incremental “knowledge base” of a landscape from drone imagery merges ideas from simultaneous localization and mapping (SLAM), structure-from-motion (SfM), and semantic segmentation. Incremental SLAM and 3D reconstruction is suggested in the ORB-SLAM2 paper by Mur-Atal and Tardos in 2017 where a 3D Map is built by estimating camera poses and reconstructing scene geometry from monocular, stereo, or RGB-D inputs. Such SLAM framework can also be extended by fusing in semantic cues to enrich the resulting map with object and scene labels The idea of including semantic information for 3D reconstruction is demonstrated by SemanticFusion written by McCormick et al for ICCV 2017 where they use a Convolutional Neural Network aka CNN for semantic segmentation as their system fuses semantic labels into a Surfel-based 3D map, thereby transforming a purely geometric reconstruction into a semantically rich representation of a scene. SemanticFusion helps to label parts of the scene – turning a raw point cloud or mesh into a knowledge base where objects, surfaces and even relationships can be recognized and queries. SfM, on the other hand, helps to stitch multi-view data into a consistent 3D-model where the techniques are particularly relevant for drone applications. Incremental SfM pipelines can populate information about a 3D space based on the data that arrives in the pipeline, but the drones can “walk the grid” around an area of interest to make sure sufficient data is captured to build the 3D-model from 0 to 100% and the progress can even be tracked. Semantic layer is not added to SfM processing, but semantic segmentation or object detection can be layered on independently overly the purely geometric data. Layering-on additional modules for say, object detection, region classification, or even reasoning over scene changes helps to start with basic geometric layouts and add optionally to build comprehensive knowledge base. Algorithms that crunch these sensor data whether they are images or LiDAR data must operate in real-time and not on batch periodic analysis. They can, however, be dedicated to specific domains such as urban monitoring, agricultural surveying, or environmental monitoring for additional context-specific knowledge.

Addendum:

• SIFT is best for high-accuracy applications like object recognition.

• ORB is ideal for real-time applications like SLAM (Simultaneous Localization and Mapping).

• SURF balances speed and accuracy, making it useful for tracking and image stitching.


Monday, June 9, 2025

 The following serves as an illustration to remove duplicates from a continuous stream of aerial images:

import cv2

import imagehash

import numpy as np

from PIL import Image

from collections import deque

from PIL import Image

import imagehash

def perceptual_hash(image_path):

    img = Image.open(image_path)

    return imagehash.phash(img)

class ImageDeduplicator:

    def __init__(self, buffer_size=100):

        """Initialize a ring buffer for tracking image hashes."""

        self.buffer_size = buffer_size

        self.hash_buffer = deque(maxlen=buffer_size)

    def compute_hash(self, image):

        """Compute perceptual hash of an image."""

        return imagehash.phash(Image.fromarray(image))

    def is_duplicate(self, image):

        """Check if the image is a duplicate."""

        img_hash = self.compute_hash(image)

        if img_hash in self.hash_buffer:

            return True

        self.hash_buffer.append(img_hash)

        return False

def process_image_stream(image_stream):

    """Process a stream of images and eliminate duplicates."""

    deduplicator = ImageDeduplicator()

    unique_images = []

    for image in image_stream:

        if not deduplicator.is_duplicate(image):

            unique_images.append(image)

    return unique_images

# Example usage

image_paths = ["image1.jpg", "image2.jpg", "image3.jpg"] # Replace with actual image paths

image_stream = [cv2.imread(img) for img in image_paths]

unique_images = process_image_stream(image_stream)

print(f"Unique images count: {len(unique_images)}")

Reference: previous article for context: 

Schema:


Sunday, June 8, 2025

 This is an illustration of extracting objects for vector similarity and deduplication:

import json
from azure.search.documents import SearchClient
from azure.core.credentials import AzureKeyCredential
import os
import re
search_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"]
api_version = os.getenv("AZURE_SEARCH_API_VERSION")
search_api_key = os.getenv("AZURE_SEARCH_ADMIN_KEY")
index_name = os.getenv("AZURE_SEARCH_INDEX_NAME", "index00")
credential = AzureKeyCredential(search_api_key)
entry_id = "003184" # "003401"
# Initialize SearchClient
search_client = SearchClient(
    endpoint=search_endpoint,
    index_name=index_name,
    credential=AzureKeyCredential(search_api_key)
)
import cv2
import numpy as np
import requests
from io import BytesIO
from azure.storage.blob import BlobClient
def read_image_from_blob(sas_url):
    """Reads an image from Azure Blob Storage using its SAS URL."""
    response = requests.get(sas_url)
    if response.status_code == 200:
        image_array = np.asarray(bytearray(response.content), dtype=np.uint8)
        image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
        return image
    else:
        raise Exception(f"Failed to fetch image. Status code: {response.status_code}")
def upload_image_to_blob(clipped_image, sas_url):
    """Uploads the clipped image to Azure Blob Storage using its SAS URL."""
    _, encoded_image = cv2.imencode(".jpg", clipped_image)
    blob_client = BlobClient.from_blob_url(sas_url)
    blob_client.upload_blob(encoded_image.tobytes(), overwrite=True)
    print("Clipped image uploaded successfully.")
def save_or_display(clipped_image, destination_file):
    cv2.imwrite(destination_file, clipped_image)
    cv2.imshow("Clipped Image", clipped_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
def clip_image(image, bounding_box):
    # Extract bounding box parameters
    x, y, width, height = bounding_box
    # Clip the region using slicing
    clipped_image = image[y:y+height, x:x+width]
    return clipped_image
def prepare_json_string_for_load(text):
  text = text.replace("\"", "'")
  text = text.replace("{'", "{\"")
  text = text.replace("'}", "\"}")
  text = text.replace(" '", " \"")
  text = text.replace("' ", "\" ")
  text = text.replace(":'", ":\"")
  text = text.replace("':", "\":")
  text = text.replace(",'", ",\"")
  text = text.replace("',", "\",")
  return re.sub(r'\n\s*', '', text)
def to_string(bounding_box):
    return f"{bounding_box['x']},{bounding_box['y']},{bounding_box['w']},{bounding_box['h']}"
# Example usage
def shred():
        source_file=entry_id
        source_sas_url = f"<origin_sas_uri>"
        # Retrieve the first 10 entries from the index
        entry = search_client.get_document(key=entry_id) # , select=["id", "description"])
        for key in entry.keys():
            print(key)
        print(f"id={entry['id']}")
        id=entry['id']
        description_text=entry['description']
        tags = entry['tags']
        title = entry['title']
        description_json = None
        try:
            description_text = prepare_json_string_for_load(entry["description"]).replace('""','')
            description_json = json.loads(description_text)
        except Exception as e:
            print(description_text)
            print(f"{entry_id}: parsing error: {e}")
        if description_json == None:
            print("Description could not be parsed.")
            return
        if description_json and description_json["_data"] and description_json["_data"]["denseCaptionsResult"] and description_json["_data"]["denseCaptionsResult"]["values"]:
            objectid = 0
            for item in description_json["_data"]["denseCaptionsResult"]["values"]:
                objectid += 1
                if objectid == 1:
                    continue
                destination_file=source_file+f"-{objectid:04d}"
                destination_sas_url = f" <destination_sas_uri> "
                box = item.get("boundingBox", None)
                if box:
                    print(f"x={box['x']}")
                    print(f"y={box['y']}")
                    print(f"w={box['w']}")
                    print(f"h={box['h']}")
                    bounding_box = (box["x"], box["y"], box["w"], box["h"])
                    # Read image from Azure Blob
                    image = read_image_from_blob(source_sas_url)
                    # Clip image
                    clipped = clip_image(image, bounding_box)
                    # Upload clipped image to Azure Blob
                    upload_image_to_blob(clipped, destination_sas_url)
                else:
                    print("no objects detected")
                break
shred()

Sample output:











Schema:









#Codingexercise: dedup processor: https://1drv.ms/w/c/d609fb70e39b65c8/ETR0Dsetyr5Kt2ORR4_tzrgBU61zj1ptL0KFWPbfX1aRSA?e=OLj75b

Saturday, June 7, 2025

 The previous article` discussed ways to enhance the index in an azure ai search vector store by promoting text that can be used queries along with semantic configuration. The following for instance is an example of semantic search on the drone images by leveraging the extracted metadata as text fields.

# Runs a semantic query (runs a BM25-ranked query and promotes the most relevant matches to the top)

results = search_client.search(query_type='semantic', semantic_configuration_name='my-semantic-config',

    search_text="Are there images that show red cars as parked?",

    select='Id,Description,title,tags,bounding_box', query_caption='extractive')

for result in results:

    print(result["@search.reranker_score"])

    print(result["id"])

    print(f"Description: {result['Description']}")

    tags = result["@search.tags"]

    if "red car" in tags:

            print(f"Title: {result["title"]}\n")

And this doesn’t just stop at query responses. Instead of the embeddings model, now we can leverage gpt-4o chat LLMs to help us generate appropriate answers to the queries given that everything is text.

Similarly, queries are text and besides the semantic search as above, we can also decompose the queries to suit the ontology we derive from the metadata including labels and tags. The way we compose lower-level queries and reusable higher-level queries helps build intelligent drone sensing applications.


Friday, June 6, 2025

 Shredding images into objects for vector search:

The previous article discussed a technique to enhance the image retrieval for drone images following the vectorize and analyze method described in the references and comes helpful regardless of zero agent, one-agent or multiple agent-based retrieval. To enhance the retrieval based on the high probability of objects being mentioned in the query, it would be even better to query based on an index of objects along with their bm25 description and semantic similarity vectors. One tip here is that the same object might be detected in multiple aerial images and different objects might be available across images spread out over temporal dimension that are more meaningful to group. This can be achieved by the following steps:

Retrieve all objects(azure search documents) with their IDs and vectors

For each object, if not already grouped:

A. Perform a vector query with its vector, excluding itself

B. Collect objects with a score above a threshold

C. Use sliding window for finding the same object repeated over consecutive images, discard duplicates

D. use reranker to find temporally distributed different objects (ids are wide apart)

E. Add these objects to a group

F. Mark all objects in the group and duplicates as processed

The following code is an illustration to populate an index with all the objects (id,description,citation,boundingBox, and vector) from all the images to do the above steps.

import json

from azure.search.documents import SearchClient

from azure.core.credentials import AzureKeyCredential

import os

import re

search_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"]

api_version = os.getenv("AZURE_SEARCH_API_VERSION")

search_api_key = os.getenv("AZURE_SEARCH_ADMIN_KEY")

index_name = os.getenv("AZURE_SEARCH_INDEX_NAME", "index00")

dest_index_name = os.getenv("AZURE_SEARCH_1024_INDEX_NAME", "index1024")

credential = AzureKeyCredential(search_api_key)

# Initialize SearchClient

search_client = SearchClient(

    endpoint=search_endpoint,

    index_name=index_name,

    credential=AzureKeyCredential(search_api_key)

)

destination_client = SearchClient(

    endpoint=search_endpoint,

    index_name=dest_index_name,

    credential=AzureKeyCredential(search_api_key)

)

def prepare_json_string_for_load(text):

  text = text.replace("\"", "'")

  text = text.replace("{'", "{\"")

  text = text.replace("'}", "\"}")

  text = text.replace(" '", " \"")

  text = text.replace("' ", "\" ")

  text = text.replace(":'", ":\"")

  text = text.replace("':", "\":")

  text = text.replace(",'", ",\"")

  text = text.replace("',", "\",")

  return re.sub(r'\n\s*', '', text)

def to_string(bounding_box):

    return f"{bounding_box['x']},{bounding_box['y']},{bounding_box['w']},{bounding_box['h']}"

page_size = 10

skip = 0

total = 17833

index = 0

while True:

    # Retrieve the first 10 entries from the index

    search_results = search_client.search("*", select=["id", "description", "vector"], top=page_size, skip = skip, include_total_count=True)

    # Process entries and shred descriptions

    flat_list = []

    if search_results.get_count() == 0:

        break

    for entry in search_results:

        entry_id = index

        index += 1

        width = 0

        height = 0

        tags = ""

        title = ""

        description_text = prepare_json_string_for_load(entry["description"]).replace('""','')

        description_json = json.loads(description_text)

        if description_json and description_json["description"]:

            title = description_json["description"]

        if description_json and description_json["_data"] and description_json["_data"]["tagsResult"] and description_json["_data"]["tagsResult"]["values"]:

            tags = ','.join([tag["name"] for tag in description_json["_data"]["tagsResult"]["values"]]).strip(",")

        # add entries at object level instead of image level

        if description_json and description_json["_data"] and description_json["_data"]["denseCaptionsResult"] and description_json["_data"]["denseCaptionsResult"]["values"]:

            for item in description_json["_data"]["denseCaptionsResult"]["values"]:

                text = item.get("text", "")

                bounding_box = item.get("boundingBox", {

                    "x": 0,

                    "y": 0,

                    "w": 0,

                    "h": 0

                })

                flat_list.append({

                    "id": index,

                    "image_id": entry_id,

                    "text": text,

                    "bounding_box": to_string(bounding_box),

                    "tags" : tags,

                    "title": title

                })

        else:

            print(f"Nothing found in entry with id:{id}")

        flat_list.append({

        "id": entry_id,

        "tags" : tags,

        "title": title

        })

     if len(flat_list) != 0:

                upload_results = destination_client.upload_documents(flat_list)

                error = ','.join([upload_result.error_message for upload_result in upload_results if upload_result.error_message]).strip(",")

                if error:

                    print(error)

                if len([upload_result.succeeded for upload_result in upload_results if upload_result.succeeded]) == page_size:

                   print(f"success in processing entries with id: {skip} to {skip + page_size}")

    skip += page_size


Vectorize and Analyze: https://1drv.ms/w/c/d609fb70e39b65c8/Eb6vxQeXGE9MsVwwdsvLSskBLgFNNuClDqAepem73pMcbQ?e=LtQasJ


Thursday, June 5, 2025

 Image retrieval enhancement:

The following is a technique to enhance the image retrieval for drone images following the vectorize and analyze method describe in the references and comes helpful regardless of zero agent, one-agent or multiple agent-based retrieval:

import json

from azure.search.documents import SearchClient

from azure.core.credentials import AzureKeyCredential

import os

import re

search_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"]

api_version = os.getenv("AZURE_SEARCH_API_VERSION")

search_api_key = os.getenv("AZURE_SEARCH_ADMIN_KEY")

index_name = os.getenv("AZURE_SEARCH_INDEX_NAME", "index00")

dest_index_name = os.getenv("AZURE_SEARCH_1024_INDEX_NAME", "index1024")

credential = AzureKeyCredential(search_api_key)

# Initialize SearchClient

search_client = SearchClient(

    endpoint=search_endpoint,

    index_name=index_name,

    credential=AzureKeyCredential(search_api_key)

)

destination_client = SearchClient(

    endpoint=search_endpoint,

    index_name=dest_index_name,

    credential=AzureKeyCredential(search_api_key)

)

def prepare_json_string_for_load(text):

  text = text.replace("\"", "'")

  text = text.replace("{'", "{\"")

  text = text.replace("'}", "\"}")

  text = text.replace(" '", " \"")

  text = text.replace("' ", "\" ")

  text = text.replace(":'", ":\"")

  text = text.replace("':", "\":")

  text = text.replace(",'", ",\"")

  text = text.replace("',", "\",")

  return re.sub(r'\n\s*', '', text)

def to_string(bounding_box):

    return f"{bounding_box['x']},{bounding_box['y']},{bounding_box['w']},{bounding_box['h']}"

page_size = 10

skip = 0

total = 17833

while True:

    # Retrieve the first 10 entries from the index

    search_results = search_client.search("*", select=["id", "description", "vector"], top=page_size, skip = skip, include_total_count=True)

    # Process entries and shred descriptions

    flat_list = []

    if search_results.get_count() == 0:

        break

    for entry in search_results:

        entry_id = entry["id"]

        width = 0

        height = 0

        tags = ""

        title = ""

        description_text = prepare_json_string_for_load(entry["description"]).replace('""','')

        description_json = json.loads(description_text)

        if description_json and description_json["description"]:

            title = description_json["description"]

        if description_json and description_json["_data"] and description_json["_data"]["tagsResult"] and description_json["_data"]["tagsResult"]["values"]:

            tags = ','.join([tag["name"] for tag in description_json["_data"]["tagsResult"]["values"]]).strip(",")

        # add entries at object level instead of image level

        # if description_json and description_json["_data"] and description_json["_data"]["denseCaptionsResult"] and description_json["_data"]["denseCaptionsResult"]["values"]:

            # for item in description_json["_data"]["denseCaptionsResult"]["values"]:

                # text = item.get("text", "")

                # bounding_box = item.get("boundingBox", {

                    # "x": 0,

                    # "y": 0,

                    # "w": 0,

                    # "h": 0

                # })

                # flat_list.append({

                    # "id": entry_id,

                    # "text": text,

                    # "bounding_box": to_string(bounding_box),

                    # "tags" : tags,

                    # "title": title

                # })

        # else:

            # print(f"Nothing found in entry with id:{id}")

        flat_list.append({

        "id": entry_id,

        "tags" : tags,

        "title": title

        })

     if len(flat_list) != 0:

                merge_results = destination_client.merge_documents(flat_list)

                error = ','.join([merge_result.error_message for merge_result in merge_results if merge_result.error_message]).strip(",")

                if error:

                    print(error)

                if len([merge_result.succeeded for merge_result in merge_results if merge_result.succeeded]) == page_size:

                   print(f"success in merging entries with id: {skip} to {skip + page_size}")

    skip += page_size

References:

Vectorize and Analyze: https://1drv.ms/w/c/d609fb70e39b65c8/Eb6vxQeXGE9MsVwwdsvLSskBLgFNNuClDqAepem73pMcbQ?e=LtQasJ