YOLOLogic (version 2.1.5, 2026-March-14)

YOLOLogic.py
 
Version: 2.1.5
   
Author: Avinash Kak (kak@purdue.edu)
 
Date: 2026-March-14
 
 

Download Version 2.1.5:  gztar  

 
     Total number of downloads (all versions): 1849
     This count is automatically updated at every rotation of
     the weblogs (normally once every two to four days)
     Last updated: Fri Mar 20 06:06:02 EDT 2026

View the main module code file in your browser  
 
Download the image datasets for YOLO  
 
 
 
CHANGES:

  Version 2.1.5:
 
    The previous version had in-place operations that interfered with the
    backpropagation of loss gradients during training. I needed to get rid of those
    for making the module compatible with the more recent versions of PyTorch.
 
  Version 2.1.4:
 
    This version includes a bugfix in the testing routine for single-instance
    object detection.  The bug, in the function run_code_for_testing_single_
    instance_detector(model), was caused by my inadvertently changing the name of
    a called function after I had finalized the code for distribution.
 
  Version 2.1.3:  
 
    This version contains significantly improved documentation about how exactly
    to install the datasets needed by the two scripts in the Examples directory of
    the YOLOLogic module.  Installing these datasets is a bit confusing because
    you have to go through two rounds of unpacking the top-level gzipped archive.
    The top-level archive packs multiple gzipped archives for the individual
    single-instance and multi-instance cases.  The improved documentation is in
    the section "THE DATASETS YOU NEED TO USE" on this page.
    
  Version 2.1.2:  
 
    Replaced the older SkipBlock with the latest version from the DLStudio
    platform.  The previous version was also throwing up run-time errors.  That
    should not be the case with the new version.
 
  Version 2.1.1:  
 
    With this version, what was previously the RegionProposalGenerator module is
    now the YOLOLogic module. This name change reflects the fact that the
    educational purpose of the module has shifted from constructing region
    proposal networks to carrying out multi-instance object detection in images
    using the YOLO logic.  This change in the primary focus of the module has
    entailed reorganizing the code base.  I have moved some of the code in the
    main YOLOLogic class to the inner class RPN.  The old region-proposal demos
    are still available through the RPN inner class.  All the YOLO based
    multi-object detection code is demoed by the script
    'multi_instance_object_detection.py' in the Examples directory of the
    distribution.  And all the region-proposal code is demoed by the scripts in
    the ExamplesRegionProposals subdirectory of the distribution.
 
  Version 2.1.0:
 
    With this version, you can now use batches of any size for YOLO learning.
    Previously, the batch size was limited to 1 for the YOLO part of the module.
    Allowing for batches required changes in the handling of problem images, such
    as the images with no meaningful objects, or the images with object bounding
    boxes with unrealistic aspect ratios.
 
  Version 2.0.8:
 
    This version constitutes a complete implementation of a YOLO multi-instance
    object detector.  In addition to the new multi-loss function that I introduced
    in the previous public release of this module, the new version includes a
    full-blown implementation of what you need for validation testing.  I should
    also mention that I have split what used to be the Examples directory in the
    distribution into two directories: Examples and ExamplesRegionProposals.  Your
    entry point for learning the YOLO implementation would be the script
    multi_instance_object_detection.py in the directory Examples.
    
  Version 2.0.6:
 
    This version incorporates a more sophisticated loss function for YOLO-based
    multi-instance object detection in images.  In the new loss function, I use
    different criteria for the different segments of the YOLO vector.  [Assigning
    an object instance in a training image to an anchor box for a cell in the
    image involves creating a "5+C"-element YOLO vector, where C is the number of
    object classes.] I now use the Binary Cross-Entropy Loss (nn.BCELoss) for the
    first element of the YOLO vector that stands for the presence or the absence
    of an object instance in a specific anchor box in a specific cell.  I use
    mean-squared-error loss (nn.MSELoss) for the next four numerical elements that
    express the precise location of the object bounding-box vis-a-vis the center
    of the cell to which the object is assigned and also for the dimensions of the
    bounding box.  Finally, I use the regular Cross-Entropy loss
    (nn.CrossEntropyLoss) for the last C elements of the YOLO vector.  Using the
    cross-entropy loss for the labeling errors required augmenting the YOLO vector
    with one additional element to express the absence of an object.
 
  Version 2.0.2:
 
    This version fixes a couple of bugs in the YOLO-based logic for multi-instance
    object detection.
 
  Version 2.0.1:
 
    This module has gone through several changes since its last public-release
    version as I was experimenting with different ways of imparting to the
    students the sudden increase in model complexity as one goes from
    single-instance object detection to multi-instance object detection.  These
    experiments led to the creation of two new datasets, PurdueDrEvalDataset and
    PurdueDrEvalMultiDataset, the former for playing with single-instance object
    detection and the latter for doing the same with multi-instance object
    detection.  The module also includes two inner classes, SingleInstanceDetector
    and YoloObjectDetector, the former a reference implementation for single
    instance object detection and the latter a YOLO reference implementation for
    multi-instance object detection. [By the way, "DrEval" in the names of the two
    datasets mentioned here has a connection with "Dr Evil" in the Austin Powers
    movies.]
 
  Version 1.0.5:
 
    In keeping with the tutorial nature of this module, this version includes
    methods that come in handy for batch-based processing of images. These methods
    carry names like "displaying_and_histogramming_ images_in_batchX()" where X is
    1, 2, and 3.  The rest of the module, especially the part that deals with
    constructing region proposals remains unchanged.
 
  Version 1.0.4:
 
    This is the first public release version of the module.
 
 
INTRODUCTION:
 
    Single-Instance vs. Multi-Instance Detection:
 
    This module was created for experimenting with the logic of object detection with
    neural networks.  On the face of it, object detection in images sounds like a
    well-defined problem that should lend itself to well-defined solutions.
    Unfortunately, the reality is otherwise.  Yes, simple examples of the problem --
    such as when the images contain single object instances and with no competing
    clutter in the background -- the problem can be solved straightforwardly with a
    neural network.  However, the object detection problems that are encountered in
    real life are rarely that simple.  A practically useful framework for object
    detection must be able to recognize and localize all possible instances of the
    objects of interest in a given image.
 
    So how does one solve the problem of multi-instance object detection and
    localization with a neural network?
 
    The last half-dozen years have seen the emergence of the following three
    competition-grade neural-network based approaches for multi-instance object
    detection: R-CNN, YOLO, and SSD.  The Preamble section of my Week 7 lecture for
    Purdue's Deep Learning class provides a brief overview of these approaches.  YOLO
    stands for "You Only Look Once" --- in contrast with R-CNN based approaches in
    which you may have to subject the images to a couple of neural networks, one for
    generating region proposals and the other for actual object detection.  The SSD
    approach, which like YOLO also processes an image only once, starts out by
    defining a set of default bounding boxes in the feature maps produced by the
    convolutional layers.  Subsequently, the output of each convo layer also provides
    predictions for the offsets between the true bounding boxes and the default
    bounding boxes.
 
    The main goal of the present module is to provide an educational example of a
    complete implementation of the original YOLO logic for multi-instance object
    detection in images.  By the original YOLO logic I mean the logic based on using
    cells and anchor boxes for encoding the position of each object of interest in an
    image.
 
    Graph-Based Algorithms for Region Proposals:
 
    A second goal of this module is to provide implementations for a couple of the
    more modern graph-based approaches for generating region proposals.  At this
    point, the reader might ask: What is a region proposal?  A region proposal is a
    blob of pixels that is highly likely to contain an object instance.  Another way
    of saying the same thing would be that the region proposals are pixel blobs that
    look different from the general background in the images.  While it is possible
    to use a neural network for generating region proposals, as demonstrated by the
    success of RPN (Region Proposal Network) in the R-CNN based approach to
    multi-instance object detection, my focus here will be on the purely algorithmic
    approaches.  I believe that becoming familiar with the non-learning based methods
    for constructing region proposals still has considerable value.  Consider, for
    example, the problem of detecting objects in satellite images where you simply do
    not have access to the amount of training data you would need for a
    neural-network based approach to work.
 
    With regard to the graph-based methods, the implementation shown here is based on
    the Selective Search (SS) algorithm for object detection as proposed by Uijlings,
    van de Sande, Gevers, and Smeulders.  The Selective Search algorithm sits on top
    of the graph-based image segmentation algorithm of Felzenszwalb and Huttenlocher
    (FH) whose re-implementation I have also provided.  In the code shown, am image
    is first processed with the FH graph-based algorithm for image segmentation to
    divide the image into pixel blobs.  Subsequently, the main logic of the SS
    algorithm is used to selectively merge the blobs on the basis of three
    properties: homogeneity of the color, grayscale variance, and texture
    homogeneity.
 
    The FH algorithm itself is based on creating a graph-based representation of an
    image in which, at the beginning, each pixel is a single vertex and the edge
    between two vertices that stand for two adjacent pixels represents the difference
    between some pixel property (such as the color difference) at the two pixels.
    Subsequently, for the vertex merging logic, each vertex U, that after the first
    iteration stands for a blob of pixels, is characterized by a property called
    Int(U), which is the largest value of the inter-pixel color difference between
    the adjacent pixels inside the blob U.  You can think of "Int" in Int(U) as
    standing for "INTer-pixel".  In order to account for the fact that, at the
    beginning, each vertex consists of only one pixel [which would not allow for the
    calculation of Int(U)], the unary property of the pixels at a vertex is extended
    from Int(U) to MInt(U) with the addition of a vertex-size dependent number equal
    to k/|C| where "k" is a user-specified parameter and |C| the cardinality of the
    set of pixels represented by the vertex U in the graph.
 
    As mentioned above, initially the edges in the graph representation of an image
    are set to the color difference between the two 8-adjacent pixels that correspond
    to two different vertices.  Subsequently, as the vertices are merged, an edge,
    E(U,V), between two vertices U and V is set to the smallest value of the
    inter-pixel color difference for two adjacent pixels that belong to the two
    different but adjacent blobs represented by the vertices. At each iteration of
    the algorithm, two vertices U and V are merged provided E(U,V) is less than the
    smaller of the MInt(U) or MInt(V) attributes at the two vertices.  My experience
    is that for most images the algorithm terminates of its own accord after a small
    number of iterations while the vertex merging condition can be satisfied.
 
    Since the algorithm is driven by the color differences between 8-adjacent
    pixels, the FH algorithm is likely to create too fine a segmentation of an
    image.  The segments produced by FH can be made larger by using the logic of
    SS that allows blobs of pixels to merge into larger blobs provided doing so
    makes sense based on the inter-blob values for mean color levels, color
    variances, texture values, etc.
 
 
INSTALLATION:
 
    The YOLOLogic class was packaged using setuptools.  For installation, execute the
    following command in the source directory (this is the directory that contains
    the setup.py file after you have downloaded and uncompressed the package):
 
            sudo python3 setup.py install
 
    On Linux distributions, this will install the module file at a location that
    looks like
 
             /usr/local/lib/python3.8/dist-packages/
 
    If you do not have root access, you have the option of working directly off the
    directory in which you downloaded the software by simply placing the following
    statements at the top of your scripts that use the YOLOLogic class:
 
            import sys
            sys.path.append( "pathname_to_YOLOLogic_directory" )
 
    To uninstall the module, simply delete the source directory, locate where the
    YOLOLogic module was installed with "locate YOLOLogic" and delete those files.
    As mentioned above, the full pathname to the installed version is likely to look
    like /usr/local/lib/python2.7/dist-packages/YOLOLogic*
 
    If you want to carry out a non-standard install of the YOLOLogic module, look up
    the on-line information on Disutils by pointing your browser to
 
              http://docs.python.org/dist/dist.html
 
USAGE:
 
    Single-Instance and Multi-Instance Detection:
 
    If you wish to experiment with the YOLO logic for multi-instance object
    detection, you would need to construct an instance of the YOLOLogic class and
    invoke the methods shown below on this instance:
 
    yolo = YOLOLogic(
                      ## The following two statements are for the single-instance script:
                      dataroot_train = "./data/Purdue_Dr_Eval_dataset_train_10000/",
                      dataroot_test = "./data/Purdue_Dr_Eval_dataset_test_1000/",
                      ## The following two statements are for the multi-instance script:
                      #  dataroot_train = "./data/Purdue_Dr_Eval_multi_dataset_train_10000/"  
                      #  dataroot_test  = "./data/Purdue_Dr_Eval_multi_dataset_test_1000/",
                      image_size = [128,128],
                      yolo_interval = 20,
                      path_saved_yolo_model = "./saved_yolo_model",
                      momentum = 0.9,
                      learning_rate = 1e-6,
                      epochs = 40,
                      batch_size = 4,
                      classes = ('Dr_Eval','house','watertower'),
                      use_gpu = True,
                  )
    yolo = YOLOLogic.YoloObjectDetector( yolo = yolo )
    yolo.set_dataloaders(train=True)
    yolo.set_dataloaders(test=True)
    model = yolo.NetForYolo(skip_connections=True, depth=8) 
    model = yolo.run_code_for_training_multi_instance_detection(model, display_images=False)
    yolo.run_code_for_training_multi_instance_detection(model, display_images = True)
    
 
    Graph-Based Algorithms for Region Proposals:
 
    To generate region proposals, you would need to construct an instance of the
    YOLOLogic class and invoke the methods shown below on this
    instance:
 
        yolo = YOLOLogic(
                       ###  The first 6 options affect only the Graph-Based part of the algo
                       sigma = 1.0,
                       max_iterations = 40,
                       kay = 0.05,
                       image_normalization_required = True,
                       image_size_reduction_factor = 4,
                       min_size_for_graph_based_blobs = 4,
                       ###  The next 4 options affect only the Selective Search part of the algo
                       color_homogeneity_thresh = [20,20,20],
                       gray_var_thresh = 16000,           
                       texture_homogeneity_thresh = 120,
                       max_num_blobs_expected = 8,
              )
        image_name = "images/mondrian.jpg"
        segmented_graph,color_map = yolo.graph_based_segmentation(image_name)
        yolo.visualize_segmentation_in_pseudocolor(segmented_graph[0], color_map, "graph_based" )
        merged_blobs, color_map = yolo.selective_search_for_region_proposals( segmented_graph, image_name )
        yolo.visualize_segmentation_with_mean_gray(merged_blobs, "ss_based_segmentation_in_bw" )
 
 
CONSTRUCTOR PARAMETERS: 
 
    Of the 10 constructor parameters listed below, the first six are meant for the FH
    algorithm and the last four for the SS algorithm.
 
    sigma: Controls the size of the Gaussian kernel used for smoothing the image
                    before its gradient is calculated.  Assuming the pixel
                    sampling interval to be unity, a sigma of 1 gives you a 7x7
                    smoothing operator with Gaussian weighting.  The default for
                    this parameter is 1.
 
    max_iterations: Sets an upper limit on the number of iterations of the
                    graph-based FH algorithm for image segmentation.
 
    kay: This is the same as the "k" parameter in the FH algorithm.  As mentioned in
                    the Introduction above, the Int(u) property of the pixels
                    represented by each vertex in the graph representation of the
                    image is extended to MInt(u) by the addition of a number k/|C|
                    where |C| is the cardinality of the set of pixels at that vertex.
 
    image_normalization_required: This applies Torchvision's image normalization
                    to the pixel values in the image.
 
    image_size_reduction_factor: As mentioned at the beginning of this document,
                    YOLOLogic is really not meant for production work.  The code is
                    pure Python and, even with that, not at all optimized.  The focus
                    of the module is primarily on easy understandability of what the
                    code is doing so that you can experiment with the algorithm
                    itself.  For the module to produce results within a reasonable
                    length of time, you can use this constructor parameter to
                    downsize the array of pixels that the module must work with.  Set
                    this parameter to a value so that the initial graph constructed
                    from the image has no more than around 3500 vertices if you don't
                    want to wait too long for the results.
 
    min_size_for_graph_based_blobs: This declares a threshold on the smallest size
                   you'd like to see (in terms of the number of pixels) in a
                   segmented blob in the output of the graph-based segmenter.  (I
                   typically use values from 1 to 4 for this parameter.)
 
    color_homogeneity_thresh: This and the next three constructor options are meant
                    specifically for the SS algorithm that sits on top of the FH
                    algorithm for further merging of the pixel blobs produced by FH.
                    This constructor option specifies the maximum allowable
                    difference between the mean color values in two pixel blobs for
                    them to be merged.
 
    gray_var_thresh: This option declares the maximum allowable difference in the
                   variances in the grayscale in two blobs if they are to be merged.
 
    texture_homogeneity_thresh: The YOLOLogic module characterizes the texture of the
                   pixels in each segmented blob by its LBP (Local Binary Patterns)
                   texture.  We want the LBP texture values for two different blobs
                   to be within the value specified by this constructor option if
                   those blobs are to be merged.
 
    max_num_blobs_expected:
 
                   If you only want to extract a certain number of the largest
                   possible blobs, you can do that by giving a value to this
                   constructor option.
 
 
Inner Classes:
 
    (1)  PurdueDrEvalDataset
 
         This is the dataset to use if you are experimenting with single-instance
         object detection.  The dataset contains three kinds of objects in its
         images: Dr. Eval, and two "objects" in his neighborhood: a house and a
         watertower.  Each 128x128 image in the dataset contains one of these objects
         after it is randomly scaled and colored. Each image also contains
         substantial structured noise in addition to 20% Gaussian noise.  Examples of
         these images are shown in the Week 7 lecture material in Purdue's Deep
         Learning class.
 
    (2)  PurdueDrEvalMultiDataset
 
         This is the dataset to use if you are experimenting with multi-instance
         object detection.  Each image in the dataset contains randomly chosen
         multiple instances of the same three kinds of objects as mentioned above:
         Dr. Eval, house, and watertower.  The number of object instances in each
         image is limited to a maximum of five.  The images contain substantial
         amount of structured noise in addition to 20% random noise.
         
         The reason for why the above two datasets have "DrEval" in their names:
         After having watched every contemporary movie at Netflix that was worth
         watching, my wife and I decided to revisit some of old movies that we had
         enjoyed a long time back.  That led us to watching again a couple of Austin
         Powers movies.  If you are too young to know what I am talking about, these
         movies are spoofs on the James Bond movies in which the great comedian Mike
         Myers plays both Austin Powers and his nemesis Dr. Evil.  Around the same
         time, I was writing code for the two datasets mentioned above.  One of the
         three objects types in these images is a human-like cartoon figure that I
         needed a name for.  So, after Dr. Evil in the movies, I decided to call this
         cartoon figure Dr Eval and to refer to the datasets as Dr Eval datasets. As
         you all know, "Eval" is an important word for people like us.  All
         programming languages provide a function with a name like "eval()".
 
    (3)  SingleInstanceDetector
 
         This provides a reference implementation for constructing a single-instance
         object detector to be used for the PurdueDrEvalDataset dataset. For the
         detection and regression network, it uses the LOADnet2 network from DLStudio
         with small modifications to account for the larger 128x128 images in the
         dataset.
 
    (4)  YoloObjectDetector   [For multi-instance detection]
 
         The code in this inner class provides an implementation for the key elements
         of the YOLO logic for multi-instance object detection.  Each training image
         is divided into a grid of cells and it is the responsibility of the cell
         that contains the center of an object bounding-box to provide at the output
         of the neural network an estimate for the exact location of the center of
         the object bounding box vis-a-vis the center of the cell.  That cell must
         also lead to an estimate for the height and the width of the bounding-box
         for the object instance.
 
 
PUBLIC METHODS:
 
    Many of these method are related to using this module for experimenting with the
    traditional graph-based algorithms for constructing region proposals in images:
 
    (1)  selective_search_for_region_proposals()
 
         This method implements elements of the Selective Search (SS) algorithm
         proposed by Uijlings, van de Sande, Gevers, and Smeulders for creating
         region proposals for object detection.  As mentioned elsewhere here, this
         algorithm sits on top of the graph based image segmentation algorithm that
         was proposed by Felzenszwalb and Huttenlocher.
 
    (2)  graph_based_segmentation()
 
         This is an implementation of the Felzenszwalb and Huttenlocher (FH)
         algorithm for graph-based segmentation of images.  At the moment, it is
         limited to working on grayscale images.
 
    (3)  display_tensor_as_image()
 
         This method converts the argument tensor into a photo image that you can
         display in your terminal screen. It can convert tensors of three different
         shapes into images: (3,H,W), (1,H,W), and (H,W), where H, for height, stands
         for the number of pixel in the vertical direction and W, for width, the same
         along the horizontal direction. When the first element of the shape is 3,
         that means that the tensor represents a color image in which each pixel in
         the (H,W) plane has three values for the three color channels.  On the other
         hand, when the first element is 1, that stands for a tensor that will be
         shown as a grayscale image.  And when the shape is just (H,W), that is
         automatically taken to be for a grayscale image.
 
    (4)  graying_resizing_binarizing()
 
         This is a demonstration of some of the more basic and commonly used image
         transformations from the torchvision.transformations module.  The large
         comment blocks are meant to serve as tutorial introduction to the syntax
         used for invoking these transformations.  The transformations shown can be
         used for converting a color image into a grayscale image, for resizing an
         image, for converting a PIL.Image into a tensor and a tensor back into an
         PIL.Image object, and so on.
 
    (5)  accessing_one_color_plane()
 
         This method shows how can access the n-th color plane of the argument color
         image.
 
    (6)  working_with_hsv_color_space()
 
         Illustrates converting an RGB color image into its HSV representation.
 
    (7)  histogramming_the_image()
 
         PyTorch based experiments with histogramming the grayscale and the color
         values in an image
 
    (8)  histogramming_and_thresholding():
 
         This method illustrates using the PyTorch functionality for histogramming
         and thresholding individual images.
 
    (9)  convolutions_with_pytorch()
 
         This method calls on torch.nn.functional.conv2d() for demonstrating a
         single image convolution with a specified kernel.
 
    (10) gaussian_smooth()
 
         This method smooths an image with a Gaussian of specified sigma.  You can do
         the same much faster by using the functionality programmed into
         torch.nn.functional.
 
    (11) visualize_segmentation_in_pseudocolor()
 
         After an image has been segmented, this method can be used to assign a
         random color to each blob in the segmented output for a better visualization
         of the segmentation.
 
    (12) visualize_segmentation_with_mean_gray()
 
         If the visualization produced by the previous method appears too chaotic,
         you can use this method to assign the mean color to each each blob in the
         output of an image segmentation algorithm.  The mean color is derived from
         the pixel values in the blob.
 
    (13) extract_image_region_interactively_by_dragging_mouse()
 
         You can use this method to apply the graph-based segmentation and the
         selective search algorithms to just a portion of your image.  This method
         extract the portion you want.  You click at the upper left corner of the
         rectangular portion of the image you are interested in and you then drag the
         mouse pointer to the lower right corner.  Make sure that you click on "save"
         and "exit" after you have delineated the area.
 
    (14) extract_image_region_interactively_through_mouse_clicks()
 
         This method allows a user to use a sequence of mouse clicks in order to
         specify a region of the input image that should be subject to further
         processing.  The mouse clicks taken together define a polygon. The method
         encloses the polygonal region by a minimum bounding rectangle, which then
         becomes the new input image for the rest of processing.
 
    (15) displaying_and_histogramming_images_in_batch1(image_dir, batch_size)
 
         This method is the first of three such methods in this module for
         illustrating the functionality of matplotlib for simultaneously displaying
         multiple images and the results obtained from them in a gridded arrangement.
         The core idea in this method is to call "plt.subplots(2,batch_size)" to
         create 'batch_size' number of subplot objects, called "axes", in the form of
         a '2xbatch_size' array. We use the first row of this grid to display each
         image in its own subplot object.  And we use the second row of the grid to
         display the histograms of the corresponding images in the first row.
 
    (16) displaying_and_histogramming_images_in_batch2(image_dir, batch_size)
 
         I now show a second approach to displaying multiple images and their
         corresponding histograms in a gridded display.  In this method we call on
         "torchvision.utils.make_grid()" to construct a grid for us.  The grid is
         created by giving an argument like "nrow=4" to it.  The grid object returned
         by the call to make_grid() is a tensor unto itself. Such a tensor object is
         converted into a numpy array so that it can be displayed by matplotlib's
         "imshow()" function.
 
    (17) displaying_and_histogramming_images_in_batch3(image_dir, batch_size)
 
         This method illustrates two things: (1) The syntax used for the 'singular'
         version of the subplot function "plt.subplot()" --- although I'll be doing
         so by actually calling "fig.add_subplot()".  And (2) How you can put
         together multiple multi-image plots by creating multiple Figure objects.
         'Figure' is the top-level container of plots in matplotlib.  This method
         creates two separate Figure objects, one as a container for all the images
         in a batch and the other as a container for all the histograms for the
         images.  The two Figure containers are displayed in two separate windows on
         your computer screen.
 
 
 
THE DATASETS YOU NEED TO USE:
 
    This section is about the dataset needs of the following two scripts in the
    Examples directory of the distribution:
 
          single_instance_object_detection.py
 
          multi_instance_object_detection.py
 
    For both these scripts, you first execute the following steps:
 
    1.  Download the archive
 
            datasets_for_YOLO.tar.gz
 
        through the link "Download the image datasets for YOLO" at the main webpage
        for this module and store the archive in the Examples directory of your
        installation of the module.
 
    2.  Subsequently, execute the following command in the Examples directory:
 
            tar zxvf datasets_for_YOLO.tar.gz
 
        This command will deposit the following archives in the "data" subdirectory
        of the Examples directory:
 
             Purdue_Dr_Eval_Dataset-clutter-10-noise-20-size-10000-train.gz  
             Purdue_Dr_Eval_Dataset-clutter-10-noise-20-size-1000-test.gz    
             Purdue_Dr_Eval_Dataset-clutter-5-noise-20-size-30-test.gz       
             Purdue_Dr_Eval_Dataset-clutter-5-noise-20-size-30-train.gz      
 
             Purdue_Dr_Eval_Multi_Dataset-clutter-10-noise-20-size-10000-train.gz
             Purdue_Dr_Eval_Multi_Dataset-clutter-10-noise-20-size-1000-test.gz
             Purdue_Dr_Eval_Multi_Dataset-clutter-10-noise-20-size-30-test.gz
             Purdue_Dr_Eval_Multi_Dataset-clutter-10-noise-20-size-30-train.gz
 
        Note the word "Multi" in the second grouping of the data archives.  These are
        the multi-instance versions of the Dr_Eval datasets.  By multi-instance I
        mean that you will have multiple instances of the three objects of interest
        (Dr. Eval, House, and Watertower) in each image.
 
        For the script "single_instance_object_detection.py", you need the first two
        archives in the first grouping of the four shown above.  And for the script
        "multi_instance_object_detection.py", you need the first two archives in in
        the second grouping of the four.
 
        In the naming convention used for the archives, the string 'clutter-10'
        means that each image will have a maximum of 10 clutter objects in it, and
        the string 'noise-20' means that I have added 20% Gaussian noise to each
        image. The string 'size-10000' means that the dataset consists of '10,000'
        images.
 
    
    The rest of the steps are specific to whether you need the datasets for the first
    of the two scripts listed at the beginning of this section or for the second.
 
 
    The Datasets You Need for "single_instance_object_detection.py":
    --------------------------------------------------------------
 
    Here are the steps:
 
    3.  Assuming you are in the Examples directory of the YOLOLogic module, now
        execute the following steps:
 
            cd data
            tar zxvf Purdue_Dr_Eval_Dataset-clutter-10-noise-20-size-10000-train.gz
 
        This will create a subdirectory named
 
            Purdue_Dr_Eval_dataset_train_10000
 
        and deposit the 10,000 training images in it.
 
    4.  For creating the test dataset, do the following in the "data" directory:
 
            tar zxvf Purdue_Dr_Eval_Dataset-clutter-10-noise-20-size-1000-test.gz
 
        This will create a subdirectory named
 
            Purdue_Dr_Eval_dataset_test_1000
 
        and deposit 1000 test images in it.
 
    IMPORTANT NOTE: The datasets used for the script
                    "single_instance_object_detection.py" do not directly provide the
                    bounding boxes (BB) for the object of interest in each image.  As
                    mentioned on Slide 82 of the Week 7 lecture, the dataloader
                    calculates the BB coordinates on the fly from the mask provided
                    for the object of interest in each image.  The Dataloader code is
                    shown on Slides 84 through 86 of the Week 7 slides.
 
    As to how the data dataset is organized for the "single_instance" case, see Slide
    81 of the Week 7 slides.
 
    
    The Datasets You Need for "multi_instance_object_detection.py":
    -------------------------------------------------------------
 
    5.  Assuming you are in the Examples directory of the YOLOLogic module, now
        execute the following steps:
 
            cd data
            tar zxvf Purdue_Dr_Eval_Multi_Dataset-clutter-10-noise-20-size-10000-train.gz
 
        Again, note the word "Multi" in the name of the data archive. The above
        command will create a subdirectory named
 
            Purdue_Dr_Eval_multi_dataset_train_10000
 
        and deposit the 10,000 training images in it.
 
    6.  For creating the test dataset, do the following in the "data" directory:
 
            tar zxvf Purdue_Dr_Eval_Multi_Dataset-clutter-10-noise-20-size-1000-test.gz
 
        This will create a subdirectory named
 
            Purdue_Dr_Eval_multi_dataset_test_1000
 
        and deposit 1000 test images in it.
 
    See the Slides 112 through 114 of my Week 7 lecture for a description of how the
    annotations are organized for the "multi" datasets mentioned above.
 
 
 
THE Examples DIRECTORY:
 
    This directory contains the following two scripts related to object detection in
    images:
 
        single_instance_object_detection.py
 
        multi_instance_object_detection.py
 
    The first script carries out single-instance detections in the images in the
    PurdueDrEvalDataset dataset and the second script carries out multi-instance
    detections in the PurdueDrEvalMultiDataset.  In the former dataset, each 128x128
    image has only one instance of a meaningful object along with structured
    artifacts and 20% random noise.  And, in the latter dataset, each image has up to
    five instances of meaningful objects along with the structured artifacts and 20%
    random noise.
 
 
 
THE ExamplesGraphBased DIRECTORY:
 
    This directory contains the following scripts for showcasing graph-based
    algorithms for constructing region proposals:
 
        selective_search.py
 
        interactive_graph_based_segmentation.py
 
        torchvision_some_basic_transformations.py    
 
        torchvision_based_image_processing.py
 
        multi_image_histogramming_and_display.py  
 
    The ExamplesGraphBased directory also illustrates the sort of region proposal
    results you can obtain with the graph-based algorithms in this module.  The
    specific illustrations are in the following subdirectories of the
    ExamplesGraphBased directory:
 
        ExamplesGraphBased/color_blobs/
 
        ExamplesGraphBased/mondrian/
 
        ExamplesGraphBased/wallpic2/
 
    Each subdirectory contains at least the following two files:
 
        selective_search.py
 
        the image file specific for that subdirectory.
 
    All you have to do is to execute selective_search.py in that directory to see the
    results on the image in that directory.
 
 
 
BUGS:
 
    Please notify the author if you encounter any bugs.  When sending email, please
    place the string 'YOLOLogic' in the subject line to get past the author's spam
    filter.
 
 
ABOUT THE AUTHOR:
 
    The author, Avinash Kak, is a professor of Electrical and Computer Engineering at
    Purdue University.  For all issues related to this module, contact the author at
    kak@purdue.edu If you send email, please place the string "YOLOLogic" in your
    subject line to get past the author's spam filter.
 
COPYRIGHT:
 
    Python Software Foundation License
 
    Copyright 2026 Avinash Kak
 
@endofdocs

 
Imported Modules
       
torchvision.transforms.functional
PIL.Image
PIL.ImageDraw
PIL.ImageFont
PIL.ImageTk
tkinter
copy
functools
glob
logging
math
torch.nn
numpy
torch.optim
os
pickle
matplotlib.pyplot
random
re
signal
sys
time
torch
torchvision
torchvision.utils
torchvision.transforms

 
Classes
       
builtins.object
YOLOLogic

 
class YOLOLogic(builtins.object)
    YOLOLogic(*args, **kwargs)
 

 
  Methods defined here:
__init__(self, *args, **kwargs)
Initialize self.  See help(type(self)) for accurate signature.
display_tensor_as_image(self, tensor, title='')
This method converts the argument tensor into a photo image that you can display
in your terminal screen. It can convert tensors of three different shapes
into images: (3,H,W), (1,H,W), and (H,W), where H, for height, stands for the
number of pixels in the vertical direction and W, for width, for the same
along the horizontal direction.  When the first element of the shape is 3,
that means that the tensor represents a color image in which each pixel in
the (H,W) plane has three values for the three color channels.  On the other
hand, when the first element is 1, that stands for a tensor that will be
shown as a grayscale image.  And when the shape is just (H,W), that is
automatically taken to be for a grayscale image.

Data descriptors defined here:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

Data and other attributes defined here:
PurdueDrEvalDataset = <class 'YOLOLogic.YOLOLogic.PurdueDrEvalDataset'>
This is the dataset to use if you are experimenting with single-instance object
detection.  The dataset contains three kinds of objects in its images:
Dr. Eval, and two "objects" in his neighborhood: a house and a watertower.
Each 128x128 image in the dataset contains one of these objects after it is
randomly scaled and colored and substantial structured noise in addition to
20% Gaussian noise.  Examples of these images are shown in Week 7 lecture
material in Purdue's Deep Learning class.
 
In order to understand the implementation of the dataloader for the Dr Eval
dataset for single-instance-based object detection, note that the top-level
directory for the dataset is organized as follows:
 
                                  dataroot
                                     |
                                     |
       ______________________________________________________________________
      |           |           |              |               |               | 
      |           |           |              |               |               |
  Dr_Eval       house      watertower    mask_Dr_Eval    mask_house     mask_watertower
      |           |           |              |               |               |
      |           |           |              |               |               |
    images      images      images      binary images    binary images   binary images
 
 
As you can see, the three main image directories are Dr_Eval, house, and
watertower. For each image in each of these directories, the mask for the
object of interest is supplied in the corresponding directory whose name
carries the prefix 'mask'.
 
For example, if you have an image named 29.jpg in the Dr_Eval directory, you
will have an image of the same name in the mask_Dr_Eval directory that will
just be the mask for the Dr_Eval object in the former image
 
As you can see, the dataset does not directly provide the bounding boxes for
object localization.  So the implementation of the __getitem__() function in
the dataloader must include code that calculates the bounding boxes from the
masks.  This you can see in the definition of the dataloader shown below.
 
Since this is a ``non-standard'' organization of the of data, the dataloader
must also provide for the indexing of the images so that they can be subject
to a fresh randomization that is carried out by PyTorch's
torch.utils.data.DataLoader class for each epoch of training.  The
index_dataset() function is provided for that purpose.
 
After the dataset is downloaded for the first time, the index_dataset()
function stores away the information as a PyTorch ``.pt'' file so that it can
be downloaded almost instantaneously at subsequent attempts.
 
One final note about the dataset: Under the hood, the dataset consists of the
pathnames to the image files --- and NOT the images themselves.  It is the
job of the multi-threaded ``workers'' provided by torch.utils.data.DataLoader
to actually download the images from those pathnames.
PurdueDrEvalMultiDataset = <class 'YOLOLogic.YOLOLogic.PurdueDrEvalMultiDataset'>
This is the dataset to use if you are experimenting with multi-instance object
detection.  As with the previous dataset, it contains three kinds of objects
in its images: Dr. Eval, and two "objects" in his neighborhood: a house and a
watertower.  Each 128x128 image in the dataset contains up to 5 instances of
these objects. The instances are randomly scaled and colored and exact number
of instances in each image is also chosen randomly. Subsequently, background
clutter is added to the images --- these are again randomly chosen
shapes. The number of clutter objects is also chosen randomly but cannot
exceed 10.  In addition to the structured clutter, I add 20% Gaussian noise
to each image.  Examples of these images are shown in Week 7 lecture material
in Purdue's Deep Learning class.
 
On account of the much richer structure of the image annotations, this
dataset is organized very differently from the previous one:
 
 
                                  dataroot
                                     |
                                     |
                         ___________________________
                        |                           |
                        |                           |
                   annotations.p                  images
 
 
Since each image is allowed to contain instances of the three different types
of "meaningful" objects, it is not possible to organize the images on the
basis of what they contain.
 
As for the annotations, the annotation for each 128x128 image is a dictionary
that contains information related to all the object instances in the image. Here
is an example of the annotation for an image that has three instances in it:
 
    annotation:  {'filename': None, 
                  'num_objects': 3, 
                  'bboxes': {0: (67, 72, 83, 118), 
                             1: (65, 2, 93, 26), 
                             2: (16, 68, 53, 122), 
                             3: None, 
                             4: None}, 
                  'bbox_labels': {0: 'Dr_Eval', 
                                  1: 'house', 
                                  2: 'watertower', 
                                  3: None, 
                                  4: None}, 
                  'seg_masks': {0: <PIL.Image.Image image mode=1 size=128x128 at 0x7F5A06C838E0>, 
                                1: <PIL.Image.Image image mode=1 size=128x128 at 0x7F5A06C837F0>, 
                                2: <PIL.Image.Image image mode=1 size=128x128 at 0x7F5A06C838B0>, 
                                3: None, 
                                4: None}
                 }
 
The annotations for the individual images are stored in a global Python
dictionary called 'all_annotations' whose keys consist of the pathnames to
the individual image files and the values the annotations dict for the
corresponding images.  The filename shown above in the keystroke diagram,
'annotations.p' is what you get by calling 'pickle.dump()' on the
'all_annotations' dictionary.
RPN = <class 'YOLOLogic.YOLOLogic.RPN'>
This class is meant specifically for experimenting with graph-based algorithms for constructing 
region proposals that may be used by a neural network for object detection and localization.
 
Classpath:    YOLOLogic  =>   RPN
SingleInstanceDetector = <class 'YOLOLogic.YOLOLogic.SingleInstanceDetector'>
This class demonstrates single-instance object detection on the images in the
PurdueDrEvalDataset dataset.  Although these image are complex, in the sense
that each image contains multiple clutter objects in addition to random
noise, nonetheless we know that each image contains only a single meaningful
object instance.  The LOADnet network used for detection is adaptation of the
the LOADnet2 network from DLStudio to the case of 128x128 sized input images.
The LOADnet network uses the SkipBlock as a building-block element for
dealing the problems caused by vanishing gradients.
YoloObjectDetector = <class 'YOLOLogic.YOLOLogic.YoloObjectDetector'>
The primary purpose of this class is to demonstrate multi-instance object detection with YOLO 
logic.  A key parameter of the logic for YOLO based detection is the variable 'yolo_interval'.  
The image gridding that is required is based on the value assigned to this variable.  The grid is 
represented by an SxS array of cells where S is the image width divided by yolo_interval. So for
images of size 128x128 and 'yolo_interval=20', you will get a 6x6 grid of cells over the image. 
Since my goal is merely to illustrate the principles of the YOLO logic, I have not bothered 
with the bottom 8 rows and the right-most 8 columns of the image that get left out of the area 
covered by such a grid.
 
An important element of the YOLO logic is defining a set of Anchor Boxes for each cell in the SxS 
grid.  The anchor boxes are characterized by their aspect ratios.  By aspect ratio I mean the
'height/width' characterization of the boxes.  My implementation provides for 5 anchor boxes for 
each cell with the following aspect ratios: 1/5, 1/3, 1/1, 3/1, 5/1.  
 
At training time, each instance in the image is assigned to that cell whose central pixel is 
closest to the center of the bounding box for the instance. After the cell assignment, the 
instance is assigned to that anchor box whose aspect ratio comes closest to matching the aspect 
ratio of the instance.
 
The assigning of an object instance to a <cell, anchor_box> pair is encoded in the form of a 
'5+C' element long YOLO vector where C is the number of classes for the object instances.  
In our cases, C is 3 for the three classes 'Dr_Eval', 'house' and 'watertower', therefore we 
end up with an 8-element vector encoding when we assign an instance to a <cell, anchor_box> 
pair.  The last C elements of the encoding vector can be thought as a one-hot representation 
of the class label for the instance.
 
The first five elements of the vector encoding for each anchor box in a cell are set as follows: 
The first element is set to 1 if an object instance was actually assigned to that anchor box. 
The next two elements are the (x,y) displacements of the center of the actual bounding box 
for the object instance vis-a-vis the center of the cell. These two displacements are expressed 
as a fraction of the width and the height of the cell.  The next two elements of the YOLO vector
are the actual height and the actual width of the true bounding box for the instance in question 
as a multiple of the cell dimension.
 
The 8-element YOLO vectors are packed into a YOLO tensor of shape (num_cells, num_anch_boxes, 8)
where num_cell is 36 for a 6x6 gridding of an image, num_anch_boxes is 5.
 
Classpath:  YOLOLogic  ->  YoloObjectDetector
canvas = None
drawEnable = 0
region_mark_coords = {}
startX = 0
startY = 0

 
Functions
       
ctrl_c_handler(signum, frame)

 
p
        __author__ = 'Avinash Kak (kak@purdue.edu)'
__copyright__ = '(C) 2026 Avinash Kak. Python Software Foundation.'
__date__ = '2026-March-14'
__url__ = 'https://engineering.purdue.edu/kak/distYOLO/YOLOLogic-2.1.5.html'
__version__ = '2.1.5'
 
Author
        Avinash Kak (kak@purdue.edu)