-
Notifications
You must be signed in to change notification settings - Fork 3
Algorithms
Affinity Propagation is a clustering algorithm that identifies a set of "exemplars" among the data points and forms clusters around these exemplars. Unlike other clustering methods (e.g., K-Means), it does not require the number of clusters to be specified beforehand. Instead, it works by exchanging messages between data points until a good set of exemplars and clusters emerge.
This example demonstrates how to perform Affinity Propagation clustering using the scikit-learn library.
# Import required libraries
from numpy import unique
from numpy import where
from sklearn.datasets import make_classification
from sklearn.cluster import AffinityPropagation
from matplotlib import pyplot
# Define the dataset
X, _ = make_classification(
n_samples=1000,
n_features=2,
n_informative=2,
n_redundant=0,
n_clusters_per_class=1,
random_state=4
)
# Define the model
model = AffinityPropagation(damping=0.9)
# Fit the model
model.fit(X)
# Assign a cluster to each example
yhat = model.predict(X)
# Retrieve unique clusters
clusters = unique(yhat)
# Create scatter plot for samples from each cluster
for cluster in clusters:
# Get row indexes for samples with this cluster
row_ix = where(yhat == cluster)
# Create scatter plot of these samples
pyplot.scatter(X[row_ix, 0], X[row_ix, 1])
# Show the plot
pyplot.show()Birch (Balanced Iterative Reducing and Clustering using Hierarchies) is a clustering algorithm designed for large datasets. It incrementally builds a clustering feature tree (CF tree) and performs clustering in a hierarchical manner. It is particularly efficient for datasets with a large number of samples due to its memory-efficient structure.
This example demonstrates how to perform Birch clustering using the scikit-learn library.
# Import required libraries
from numpy import unique
from numpy import where
from sklearn.datasets import make_classification
from sklearn.cluster import Birch
from matplotlib import pyplot
# Define the dataset
X, _ = make_classification(
n_samples=1000,
n_features=2,
n_informative=2,
n_redundant=0,
n_clusters_per_class=1,
random_state=4
)
# Define the model
model = Birch(threshold=0.01, n_clusters=2)
# Fit the model
model.fit(X)
# Assign a cluster to each example
yhat = model.predict(X)
# Retrieve unique clusters
clusters = unique(yhat)
# Create scatter plot for samples from each cluster
for cluster in clusters:
# Get row indexes for samples with this cluster
row_ix = where(yhat == cluster)
# Create scatter plot of these samples
pyplot.scatter(X[row_ix, 0], X[row_ix, 1])
# Show the plot
pyplot.show()DBSCAN (Density-Based Spatial Clustering of Applications with Noise) is a density-based clustering algorithm. It groups together data points that are close to each other based on a distance measurement and a minimum number of points. It is particularly useful for discovering clusters of arbitrary shape and for identifying noise (outliers) in the data.
DBSCAN works by defining:
- eps (epsilon): The maximum distance between two samples for them to be considered as in the same neighborhood.
- min_samples: The minimum number of points required to form a dense region (a cluster).
This example demonstrates how to perform DBSCAN clustering using the scikit-learn library.
# Import required libraries
from numpy import unique
from numpy import where
from sklearn.datasets import make_classification
from sklearn.cluster import DBSCAN
from matplotlib import pyplot
# Define the dataset
X, _ = make_classification(
n_samples=1000,
n_features=2,
n_informative=2,
n_redundant=0,
n_clusters_per_class=1,
random_state=4
)
# Define the model
model = DBSCAN(eps=0.30, min_samples=9)
# Fit model and predict clusters
yhat = model.fit_predict(X)
# Retrieve unique clusters
clusters = unique(yhat)
# Create scatter plot for samples from each cluster
for cluster in clusters:
# Get row indexes for samples with this cluster
row_ix = where(yhat == cluster)
# Create scatter plot of these samples
pyplot.scatter(X[row_ix, 0], X[row_ix, 1])
# Show the plot
pyplot.show()This example demonstrates the use of optical flow to track motion between two consecutive frames of a video using the Farneback method. The Farneback method is a dense optical flow algorithm that calculates the motion between two images based on polynomial expansion. The resulting flow is visualized using HSV color representation, where the hue represents the direction of motion and the value represents the magnitude.
This technique is often used in computer vision for motion detection, video stabilization, and object tracking.
import cv2
import numpy as np
# Open the video file
cap = cv2.VideoCapture('GOPR1745.avi')
# Read the first frame and convert it to grayscale
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
# Initialize the HSV image for visualization
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255
# Initialize a counter
count = 0
while(1):
# Read the next frame
ret, frame2 = cap.read()
# Convert the next frame to grayscale
next = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
# Calculate optical flow using Farneback method
flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 10, 1.2, 0)
# Convert flow to polar coordinates (magnitude and angle)
mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])
# Set hue to the angle (direction of motion)
hsv[..., 0] = ang * 180Edge detection is a technique used to identify boundaries of objects in an image. This example demonstrates two common edge detection methods:
- Sobel Edge Detection: Detects edges based on gradients in the X and Y directions.
- Canny Edge Detection: A multi-step edge detection algorithm that detects a wide range of edges in an image.
In this code, we read an image, convert it to grayscale, apply a Gaussian blur for noise reduction, and then perform edge detection using both Sobel and Canny methods.
import cv2
# Read the original image
img = cv2.imread('test.jpg')
# Display original image
cv2.imshow('Original', img)
cv2.waitKey(0)
# Convert to grayscale
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Blur the image for better edge detection
img_blur = cv2.GaussianBlur(img_gray, (3, 3), 0)
# Sobel Edge Detection
sobelx = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=1, dy=0, ksize=5) # Sobel Edge Detection on the X axis
sobely = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=0, dy=1, ksize=5) # Sobel Edge Detection on the Y axis
sobelxy = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=1, dy=1, ksize=5) # Combined X and Y Sobel Edge Detection
# Display Sobel Edge Detection Images
cv2.imshow('Sobel X', sobelx)
cv2.waitKey(0)
cv2.imshow('Sobel Y', sobely)
cv2.waitKey(0)
cv2.imshow('Sobel X Y using Sobel() function', sobelxy)
cv2.waitKey(0)
# Canny Edge Detection
edges = cv2.Canny(image=img_blur, threshold1=100, threshold2=200) # Canny Edge Detection
# Display Canny Edge Detection Image
cv2.imshow('Canny Edge Detection', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()This example demonstrates how to use the ORB (Oriented FAST and Rotated BRIEF) algorithm for feature detection and description in images. ORB is a fast and efficient feature detection method that combines the FAST keypoint detector and the BRIEF descriptor, with improvements to handle rotation and scale variations. It is commonly used in applications such as object recognition, image stitching, and augmented reality.
In this code, we load two images: one as the query image and the other as the scene where the query image might appear. We then apply the ORB detector to both images to extract keypoints and descriptors.
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# Load the images: queryImage and trainImage
img1 = cv.imread('box.png', cv.IMREAD_GRAYSCALE) # queryImage
img2 = cv.imread('box_in_scene.png', cv.IMREAD_GRAYSCALE) # trainImage
# Initiate ORB detector
orb = cv.ORB_create()
# Find the keypoints and descriptors with ORB
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)In this example, we demonstrate how to perform feature matching between two images using the SIFT (Scale-Invariant Feature Transform) algorithm along with FLANN (Fast Library for Approximate Nearest Neighbors). SIFT detects and describes keypoints in images, and FLANN is used to efficiently match the descriptors between the images.
The images are compared by finding the best matching keypoints, and the results are displayed by drawing lines between the matching keypoints in both images.
import cv2
import numpy as np
# Load the images
img1 = cv2.imread('../img/taekwonv1.jpg') # Query image
img2 = cv2.imread('../img/figures.jpg') # Train image
# Convert images to grayscale
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# Create SIFT detector
detector = cv2.xfeatures2d.SIFT_create()
# Extract keypoints and descriptors
kp1, desc1 = detector.detectAndCompute(gray1, None)
kp2, desc2 = detector.detectAndCompute(gray2, None)
# Set index parameters and search parameters ---①
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
# Create Flann-based matcher ---③
matcher = cv2.FlannBasedMatcher(index_params, search_params)
# Compute matches ---④
matches = matcher.match(desc1, desc2)
# Draw matches
res = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, \
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
# Display the result
cv2.imshow('Flann + SIFT', res)
cv2.waitKey()
cv2.destroyAllWindows()This script demonstrates how to perform color segmentation in an image using OpenCV. It allows the user to select a specific color (e.g., blue, white, or black) and then filters out pixels that match the selected color. The result is displayed alongside the original image and a mask that shows only the chosen color.
The segmentation is performed by converting the image from BGR to HSV color space, and then applying a threshold to isolate the pixels within a defined color range.
import cv2
import numpy as np
import imutils
def color_seg(choice):
if choice == 'blue':
lower_hue = np.array([100,30,30])
upper_hue = np.array([150,148,255])
elif choice == 'white':
lower_hue = np.array([0,0,0])
upper_hue = np.array([0,0,255])
elif choice == 'black':
lower_hue = np.array([0,0,0])
upper_hue = np.array([50,50,100])
return lower_hue, upper_hue
# Take each frame
frame = cv2.imread('images/black0.jpg')
#frame = cv2.imread('images/road_1.jpg')
frame = imutils.resize(frame, height = 300)
chosen_color = 'black'
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of a color in HSV
lower_hue, upper_hue = color_seg(chosen_color)
# Threshold the HSV image to get only the chosen color
mask = cv2.inRange(hsv, lower_hue, upper_hue)
cv2.imshow('frame', frame)
cv2.imshow('mask', mask)
cv2.waitKey(0)This script demonstrates how to perform a perspective transformation on an image using homography. Specifically, it warps a source image to align with a destination image. This process is typically used in tasks like object detection, image stitching, and augmented reality.
The homography is computed based on the corresponding points (the four corners of the book in both images). Once the homography is calculated, the source image is warped to align with the destination image.
import cv2
import numpy as np
if __name__ == '__main__' :
# Read source image.
im_src = cv2.imread('book2.jpg')
# Four corners of the book in source image
pts_src = np.array([[141, 131], [480, 159], [493, 630],[64, 601]])
# Read destination image.
im_dst = cv2.imread('book1.jpg')
# Four corners of the book in destination image.
pts_dst = np.array([[318, 256],[534, 372],[316, 670],[73, 473]])
# Calculate Homography
h, status = cv2.findHomography(pts_src, pts_dst)
# Warp source image to destination based on homography
im_out = cv2.warpPerspective(im_src, h, (im_dst.shape[1],im_dst.shape[0]))
# Display images
cv2.imshow("Source Image", im_src)
cv2.imshow("Destination Image", im_dst)
cv2.imshow("Warped Source Image", im_out)
cv2.waitKey(0)This script demonstrates how to use ORB (Oriented FAST and Rotated BRIEF) for feature detection and matching between two images. The matched keypoints are used to compute a homography matrix that aligns the source image with the destination image. This process is useful in applications like image stitching, object recognition, and panorama creation.
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
from google.colab import files
# Load images
img1 = cv2.imread('image1.jpg')
img2 = cv2.imread('image2.jpg')
# Convert images to grayscale
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# Initialize ORB detector
orb = cv2.ORB_create()
# Find keypoints and descriptors with ORB
kp1, des1 = orb.detectAndCompute(gray1, None)
kp2, des2 = orb.detectAndCompute(gray2, None)
# Use BFMatcher to find matches between descriptors
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
# Sort matches by distance (best matches first)
matches = sorted(matches, key=lambda x: x.distance)
# Draw top 10 matches
img_matches = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
# Extract location of good matches
pts1 = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
pts2 = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
# Find the homography matrix using RANSAC
H, mask = cv2.findHomography(pts1, pts2, cv2.RANSAC, 5.0)
# Use the homography matrix to warp img1 to match img2
height, width, _ = img2.shape
img1_aligned = cv2.warpPerspective(img1, H, (width, height))
# Show the aligned image and matches (using cv2_imshow for Colab)
cv2_imshow(img1_aligned) # This will display the aligned image
cv2_imshow(img_matches) # This will display the matched keypoints
# Save the output images
cv2.imwrite('aligned_image.jpg', img1_aligned)
cv2.imwrite('matches.jpg', img_matches)
# Download the saved images to your local system
files.download('matches.jpg')This script demonstrates the use of Harris Corner Detection to identify and mark corner points in an image. The technique highlights significant points in an image where the intensity gradient is large in multiple directions. These points can be useful for tasks like image matching, object recognition, and feature tracking.
import cv2
import numpy as np
# Read the image
img = cv2.imread('../img/house.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Harris Corner Detection ---①
corner = cv2.cornerHarris(gray, 2, 3, 0.04)
# Get coordinates where the variation result exceeds 10% of the maximum value ---②
coord = np.where(corner > 0.1 * corner.max())
coord = np.stack((coord[1], coord[0]), axis=-1)
# Draw circles at corner coordinates ---③
for x, y in coord:
cv2.circle(img, (x, y), 5, (0, 0, 255), 1, cv2.LINE_AA)
# Normalize the variation to 0-255 for visualization ---④
corner_norm = cv2.normalize(corner, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
# Convert to BGR for display
corner_norm = cv2.cvtColor(corner_norm, cv2.COLOR_GRAY2BGR)
# Merge the normalized image and the original image
merged = np.hstack((corner_norm, img))
# Display the result
cv2.imshow('Harris Corner', merged)
cv2.waitKey()
cv2.destroyAllWindows()This script demonstrates the use of ORB (Oriented FAST and Rotated BRIEF) for detecting keypoints in an image. ORB is a feature detection algorithm that is fast and robust, often used for tasks like image matching, object recognition, and 3D reconstruction.
import cv2
# Read the image in grayscale
image = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)
# Create ORB detector
orb = cv2.ORB_create()
# Detect keypoints using ORB
keypoints = orb.detect(image, None)
# Draw the keypoints on the image
output = cv2.drawKeypoints(image, keypoints, None)
# Display the result
cv2.imshow("ORB Keypoints", output)
cv2.waitKey(0)
cv2.destroyAllWindows()This script demonstrates the use of SIFT (Scale-Invariant Feature Transform) for detecting keypoints in an image. SIFT is a popular feature detection and description algorithm that is invariant to scaling, rotation, and partially invariant to affine transformations and noise.
import cv2
# Read the image in grayscale
image = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)
# Create SIFT detector
sift = cv2.SIFT_create()
# Detect keypoints using SIFT
keypoints = sift.detect(image, None)
# Draw the keypoints on the image
output = cv2.drawKeypoints(image, keypoints, None)
# Display the result
cv2.imshow("SIFT Keypoints", output)
cv2.waitKey(0)
cv2.destroyAllWindows()This script demonstrates the use of the Shi-Tomasi corner detection method for detecting corners in an image. The Shi-Tomasi method is an efficient corner detection algorithm used to find points in an image where there are significant changes in intensity in both horizontal and vertical directions.
import cv2
import numpy as np
# Read the image
img = cv2.imread('../img/house.jpg')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Shi-Tomasi corner detection method
corners = cv2.goodFeaturesToTrack(gray, 80, 0.01, 10)
# Convert floating-point coordinates to integer coordinates
corners = np.int32(corners)
# Draw circles at corner coordinates
for corner in corners:
x, y = corner[0]
cv2.circle(img, (x, y), 5, (0, 0, 255), 1, cv2.LINE_AA)
# Display the result
cv2.imshow('Corners', img)
cv2.waitKey()
cv2.destroyAllWindows()This script uses OpenCV's pre-trained Haar Cascade classifier to detect faces in an image. The Haar Cascade method is an effective object detection technique that uses positive and negative image samples to train a classifier.
import cv2
# Load the pre-trained Haar Cascade classifier for face detection
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
# Load the image
img = cv2.imread('../img/group.jpg')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Detect faces
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
# Draw rectangles around detected faces
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
# Show the result
cv2.imshow('Detected Faces', img)
cv2.waitKey(0)
cv2.destroyAllWindows()This script demonstrates how to use OpenCV’s deep learning (DNN) module to perform object detection using the SSD (Single Shot Multibox Detector) model with MobileNet architecture. The model can detect various objects such as people, cars, bicycles, etc.
import cv2
import numpy as np
# Load the SSD model
net = cv2.dnn.readNetFromCaffe('MobileNetSSD_deploy.prototxt', 'MobileNetSSD_deploy.caffemodel')
# Class labels for SSD MobileNet
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
"bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
"dog", "horse", "motorbike", "person", "pottedplant", "sheep",
"sofa", "train", "tvmonitor"]
# Load the image
image = cv2.imread('../img/street.jpg')
(h, w) = image.shape[:2]
# Prepare the image for SSD
blob = cv2.dnn.blobFromImage(image, scalefactor=0.007843, size=(300, 300), mean=(127.5, 127.5, 127.5), swapRB=True, crop=False)
net.setInput(blob)
# Perform detection
detections = net.forward()
# Draw detected objects
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > 0.2: # Confidence threshold
idx = int(detections[0, 0, i, 1])
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")
label = f"{CLASSES[idx]}: {confidence:.2f}"
cv2.rectangle(image, (startX, startY), (endX, endY), (0, 255, 0), 2)
cv2.putText(image, label, (startX, startY - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# Show the result
cv2.imshow('SSD Object Detection', image)
cv2.waitKey(0)
cv2.destroyAllWindows()This script demonstrates how to use OpenCV’s DNN module to perform object detection using the YOLO (You Only Look Once) model. YOLO is a state-of-the-art object detection model that can detect multiple objects in real-time with high accuracy.
import cv2
import numpy as np
# Load YOLO model and class labels
net = cv2.dnn.readNet('yolov3.weights', 'yolov3.cfg')
with open('coco.names', 'r') as f:
classes = f.read().strip().split('\n')
# Load the image
img = cv2.imread('../img/street.jpg')
height, width, _ = img.shape
# Prepare the image for YOLO
blob = cv2.dnn.blobFromImage(img, scalefactor=1/255.0, size=(416, 416), swapRB=True, crop=False)
net.setInput(blob)
# Get YOLO layer names and output
layer_names = net.getUnconnectedOutLayersNames()
layer_outputs = net.forward(layer_names)
# Process YOLO outputs
boxes, confidences, class_ids = [], [], []
for output in layer_outputs:
for detection in output:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.5: # Confidence threshold
box = detection[0:4] * np.array([width, height, width, height])
(center_x, center_y, w, h) = box.astype("int")
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, int(w), int(h)])
confidences.append(float(confidence))
class_ids.append(class_id)
# Apply Non-Max Suppression
indices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
# Draw detected objects
for i in indices:
i = i[0]
box = boxes[i]
(x, y, w, h) = box
label = f"{classes[class_ids[i]]}: {confidences[i]:.2f}"
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(img, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# Show the result
cv2.imshow('YOLO Object Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()This script demonstrates how to perform saliency detection in an image using two different methods from OpenCV’s saliency module:
- Static Saliency Spectral Residual
- Static Saliency Fine Grained
Saliency detection highlights the most visually significant parts of an image, which can be useful for tasks like object detection, segmentation, and focus regions.
import cv2
# Static Saliency Spectral Residual Method
imgpath = r'Content Image.jpg'
image = cv2.imread(imgpath)
saliency = cv2.saliency.StaticSaliencySpectralResidual_create()
(success, saliencyMap) = saliency.computeSaliency(image)
saliencyMap = (saliencyMap * 255).astype("uint8")
cv2.imshow("Image", image)
cv2.imshow("Output", saliencyMap)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Static Saliency Fine Grained Method
imgpath = r'Content Image.jpg'
image = cv2.imread(imgpath)
saliency = cv2.saliency.StaticSaliencyFineGrained_create()
(success, saliencyMap) = saliency.computeSaliency(image)
# Set threshold for saliency map
threshMap = cv2.threshold(saliencyMap.astype("uint8"), 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("Image", image)
cv2.imshow("Output", saliencyMap)
# cv2.imshow("Thresh", threshMap)
cv2.waitKey(0)The display_side_by_side function allows you to display multiple pandas DataFrames side by side within a Jupyter notebook. You can optionally provide titles for each DataFrame. If no titles are provided, it defaults to an empty string. The function formats the DataFrames using HTML to ensure they are displayed inline.
from IPython.display import display_html
from itertools import chain, cycle
def display_side_by_side(*args, titles=cycle([''])):
html_str = ''
for df, title in zip(args, chain(titles, cycle(['</br>']))):
html_str += '<th style="text-align:center"><td style="vertical-align:top">'
html_str += f'<h2 style="text-align: center;">{title}</h2>'
html_str += df.to_html().replace('table', 'table style="display:inline"')
html_str += '</td></th>'
display_html(html_str, raw=True)