Related
I am using opencvs findContour to find the points to describe an image made up of lines (not polygons) as such:
cv::findContours(src, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);.
If I understand correctly, the "cv2.connectedComponents" method gives what you are looking for. It assigns a label for each point in your image, the label is the same if points are connected. By doing this assignment there is no duplication happening. So, if your lines are one pixel wide (e.g output of an edge detector or a thinning operator) you get one point per location.
Edit:
As per the OP request, lines should be 1-pixel wide. To achieve this a thinning operation is applied before finding connected components. Steps images have been added too.
Please note that each connected component points are sorted in ascending order of y cords.
img_path = "D:/_temp/fig.png"
output_dir = 'D:/_temp/'
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
total_white_pixels = cv2.countNonZero(img)
print ("Total White Pixels Before Thinning = ", total_white_pixels)
cv2.imwrite(output_dir + '1-thresholded.png', img)
#apply thinning -> each line is one-pixel wide
img = cv2.ximgproc.thinning(img)
cv2.imwrite(output_dir + '2-thinned.png', img)
total_white_pixels = cv2.countNonZero(img)
print ("Total White Pixels After Thinning = ", total_white_pixels)
no_ccs, labels = cv2.connectedComponents(img)
label_pnts_dic = {}
colored = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
i = 1 # skip label 0 as it corresponds to the backgground points
sum_of_cc_points = 0
while i < no_ccs:
label_pnts_dic[i] = np.where(labels == i) #where return tuple(list of x cords, list of y cords)
colored[label_pnts_dic[i]] = (random.randint(100, 255), random.randint(100, 255), random.randint(100, 255))
i +=1
cv2.imwrite(output_dir + '3-colored.png', colored)
print ("First ten points of label-1 cc: ")
for i in range(10):
print ("x: ", label_pnts_dic[1][1][i], "y: ", label_pnts_dic[1][0][i])
Output:
Total White Pixels Before Thinning = 6814
Total White Pixels After Thinning = 2065
First ten points of label-1 cc:
x: 312 y: 104
x: 313 y: 104
x: 314 y: 104
x: 315 y: 104
x: 316 y: 104
x: 317 y: 104
x: 318 y: 104
x: 319 y: 104
x: 320 y: 104
x: 321 y: 104
Images:
1.Thresholded
Thinned
Colored Components
Edit2:
After a discussion with OP, I understood that having a list of (scattered) points is not enough. Points should be ordered so that they could be traced. To achieve that new logic should be introduced after applying thinning to the image.
Find extreme points (points with a single 8-connectivity neighbor)
Find connector points (points with 3-ways connectivity)
Find simple points (all other points)
Start tracing from an extreme point until reaching another extreme point or a connector one.
Extract the traveled path.
Check whether a connector point has turned into a simple point and update its status.
Repeat
Check if there are any closed-loops of simple points that have not been reached from any extreme point, extract each closed-loop as an additional waypoint.
Code for extreme/connector/simple point classification
def filter_neighbors(ns):
i = 0
while i < len(ns):
j = i + 1
while j < len(ns):
if (ns[i][0] == ns[j][0] and abs(ns[i][1] - ns[j][1]) <= 1) or (ns[i][1] == ns[j][1] and abs(ns[i][0] - ns[j][0]) <= 1):
del ns[j]
break
j += 1
i += 1
def sort_points_types(pnts):
extremes = []
connections = []
simple = []
for i in range(pnts.shape[0]):
neighbors = []
for j in range (pnts.shape[0]):
if i == j: continue
if abs(pnts[i, 0] - pnts[j, 0]) <= 1 and abs(pnts[i, 1] - pnts[j, 1]) <= 1:#8-connectivity check
neighbors.append(pnts[j])
filter_neighbors(neighbors)
if len(neighbors) == 1:
extremes.append(pnts[i])
elif len(neighbors) == 2:
simple.append(pnts[i])
elif len(neighbors) > 2:
connections.append(pnts[i])
return extremes, connections, simple
img_path = "D:/_temp/fig.png"
output_dir = 'D:/_temp/'
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
img = cv2.ximgproc.thinning(img)
pnts = cv2.findNonZero(img)
pnts = np.squeeze(pnts)
ext, conn, simple = sort_points_types(pnts)
for p in conn:
cv2.circle(img, (p[0], p[1]), 5, 128)
for p in ext:
cv2.circle(img, (p[0], p[1]), 5, 128)
cv2.imwrite(output_dir + "6-both.png", img)
print (len(ext), len(conn), len(simple))
Edit3:
A much more efficient implementation for classifying the points in a single pass by checking neighbors in a kernel-like way, thanks to eldesgraciado!
Note: Before calling this method the image should be padded with one pixel to avoid border checks or equivalently blackout pixels at the border.
def sort_points_types(pnts, img):
extremes = []
connections = []
simple = []
for p in pnts:
x = p[0]
y = p[1]
n = []
if img[y - 1,x] > 0: n.append((y-1, x))
if img[y - 1,x - 1] > 0: n.append((y-1, x - 1))
if img[y - 1,x + 1] > 0: n.append((y-1, x + 1))
if img[y,x - 1] > 0: n.append((y, x - 1))
if img[y,x + 1] > 0: n.append((y, x + 1))
if img[y + 1,x] > 0: n.append((y+1, x))
if img[y + 1,x - 1] > 0: n.append((y+1, x - 1))
if img[y + 1,x + 1] > 0: n.append((y+1, x + 1))
filter_neighbors(n)
if len(n) == 1:
extremes.append(p)
elif len(n) == 2:
simple.append(p)
elif len(n) > 2:
connections.append(p)
return extremes, connections, simple
An image visualizing extreme and connector points:
I'd like to sum areas of the largest five contours after sorted or all of them if less than five.
The robot follows the people's base on the color but sometimes people have the same color and I'd like to choose one from them using the area. I used this line for two contours but this method is not good area1 = cv2.contourArea(cnts[0]) + cv2.contourArea(cnts[1])
Full code:
import cv2
import numpy as np
from imutils.video import FPS
import time
cap = cv2.VideoCapture(0)
width = cap.get(3) # float
height = cap.get(4) # float
print width, height
time.sleep(2.0)
fps = FPS().start()
while (1):
_, img = cap.read()
if _ is True:
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
else:
continue
black_lower = np.array([0,0,0], np.uint8)
black_upper = np.array([180,255,30], np.uint8)
black = cv2.inRange(hsv, black_lower, black_upper)
kernal = np.ones((5, 5), "uint8")
black = cv2.dilate(black, kernal)
res_black = cv2.bitwise_and(img, img, mask=black)
# Tracking black
(_, contours, hierarchy) = cv2.findContours(black, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(contours, key=cv2.contourArea, reverse=True)[:2000] # get largest 2000 contour area
area1 = cv2.contourArea(cnts[0]) + cv2.contourArea(cnts[1])
# area2 = cv2.contourArea(cnts[0])
# total = area1 +area2
print 'area', area1, type(cnts)
rects = []
print len(cnts) , type(cnts[1])
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
x, y, w, h = cv2.boundingRect(approx)
if h >= 15:
rect = (x, y, w, h)
rects.append(rect)
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0), 2)
cv2.putText(img, "Black Colour", (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 0))
cv2.imshow("Color Tracking", img)
if cv2.waitKey(10) & 0xFF == ord('q'):
cap.release()
cv2.destroyAllWindows()
break
Any help or suggestions would be appreciated.
You can sum them using list = [], but maybe you face another issue, sum of areas for all people.
import cv2
import numpy as np
from imutils.video import FPS
import time
cap = cv2.VideoCapture(0)
width = cap.get(3) # float
height = cap.get(4) # float
print width, height
time.sleep(2.0)
fps = FPS().start()
while (1):
_, img = cap.read()
if _ is True:
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
else:
continue
black_lower = np.array([0,0,0], np.uint8)
black_upper = np.array([180,255,30], np.uint8)
black = cv2.inRange(hsv, black_lower, black_upper)
kernal = np.ones((5, 5), "uint8")
black = cv2.dilate(black, kernal)
res_black = cv2.bitwise_and(img, img, mask=black)
# Tracking black
(_, contours, hierarchy) = cv2.findContours(black, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(contours, key=cv2.contourArea, reverse=True)[:5] # get largest five contour area
areas = []
for contour in cnts:
area = cv2.contourArea(contour)
if area > 300:
areas.append(area)
x, y, w, h = cv2.boundingRect(contour)
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0), 2)
cv2.putText(img, "Black Colour", (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 0))
a = sum(areas)
print areas
print a
cv2.imshow("Color Tracking", img)
if cv2.waitKey(10) & 0xFF == ord('q'):
cap.release()
cv2.destroyAllWindows()
break
you can use just this line:
area = sum([cv2.contourArea(cnt) for cnt in sorted(cnts, key=cv2.contourArea, reverse=True)[:5]])
I will add the full code to compare between them.
import cv2
import numpy as np
from imutils.video import FPS
import time
cap = cv2.VideoCapture(0)
width = cap.get(3) # float
height = cap.get(4) # float
print width, height
time.sleep(2.0)
fps = FPS().start()
while (1):
_, img = cap.read()
if _ is True:
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
else:
continue
black_lower = np.array([0,0,0], np.uint8)
black_upper = np.array([180,255,30], np.uint8)
black = cv2.inRange(hsv, black_lower, black_upper)
kernal = np.ones((5, 5), "uint8")
black = cv2.dilate(black, kernal)
res_black = cv2.bitwise_and(img, img, mask=black)
# Tracking black
(_, contours, hierarchy) = cv2.findContours(black, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(contours, key=cv2.contourArea, reverse=True)[:5] # get largest five contour area
area = sum([cv2.contourArea(cnt) for cnt in sorted(cnts, key=cv2.contourArea, reverse=True)[:5]])
print 'area_method1', area
areas = []
for contour in cnts:
area = cv2.contourArea(contour)
if area > 300:
areas.append(area)
x, y, w, h = cv2.boundingRect(contour)
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0), 2)
cv2.putText(img, "Black Colour", (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 0))
a = sum(areas)
# print areas
print 'area_method2', a
cv2.imshow("Color Tracking", img)
if cv2.waitKey(10) & 0xFF == ord('q'):
cap.release()
cv2.destroyAllWindows()
break
I am trying to find a 3D model from 2 images taken from the same camera using OpenCV with C++. I followed this method. I am still not able to rectify mistake in R and T computation.
Image 1: With Background Removed for eliminating mismatches
Image 2: Translated only in X direction wrt Image 1 With Background Removed for eliminating mismatches
I have found the Intrinsic Camera Matrix (K) using MATLAB Toolbox. I found it to be :
K=
[3058.8 0 -500
0 3057.3 488
0 0 1]
All image matching keypoints (using SIFT and BruteForce Matching, Mismatches Eliminated) were aligned wrt center of image as follows:
obj_points.push_back(Point2f(keypoints1[symMatches[i].queryIdx].pt.x - image1.cols / 2, -1 * (keypoints1[symMatches[i].queryIdx].pt.y - image1.rows / 2)));
scene_points.push_back(Point2f(keypoints2[symMatches[i].trainIdx].pt.x - image1.cols / 2, -1 * (keypoints2[symMatches[i].trainIdx].pt.y - image1.rows / 2)));
From Point Correspondeces, I found out Fundamental Matrix Using RANSAC in OpenCV
Fundamental Matrix:
[0 0 -0.0014
0 0 0.0028
0.00149 -0.00572 1 ]
Essential Matrix obtained using:
E = (camera_Intrinsic.t())*f*camera_Intrinsic;
E obtained:
[ 0.0094 36.290 1.507
-37.2245 -0.6073 14.71
-1.3578 -23.545 -0.442]
SVD of E:
E.convertTo(E, CV_32F);
Mat W = (Mat_<float>(3, 3) << 0, -1, 0, 1, 0, 0, 0, 0, 1);
Mat Z = (Mat_<float>(3, 3) << 0, 1, 0, -1, 0, 0, 0, 0, 0);
SVD decomp = SVD(E);
Mat U = decomp.u;
Mat Lambda = decomp.w;
Mat Vt = decomp.vt;
New Essential Matrix for epipolar constraint:
Mat diag = (Mat_<float>(3, 3) << 1, 0, 0, 0, 1, 0, 0, 0, 0);
Mat new_E = U*diag*Vt;
SVD new_decomp = SVD(new_E);
Mat new_U = new_decomp.u;
Mat new_Lambda = new_decomp.w;
Mat new_Vt = new_decomp.vt;
Rotation from SVD:
Mat R1 = new_U*W*new_Vt;
Mat R2 = new_U*W.t()*new_Vt;
Translation from SVD:
Mat T1 = (Mat_<float>(3, 1) << new_U.at<float>(0, 2), new_U.at<float>(1, 2), new_U.at<float>(2, 2));
Mat T2 = -1 * T1;
I was getting the R matrices to be :
R1:
[ -0.58 -0.042 0.813
-0.020 -0.9975 -0.066
0.81 -0.054 0.578]
R2:
[ 0.98 0.0002 0.81
-0.02 -0.99 -0.066
0.81 -0.054 0.57 ]
Translation Matrices:
T1:
[0.543
-0.030
0.838]
T2:
[-0.543
0.03
-0.83]
Please clarify wherever there is a mistake.
This 4 sets of P2 matrix R|T with P1=[I] are giving incorrect triangulated models.
Also, I think the T matrix obtained is incorrect, as it was supposed to be only x shift and no z shift.
When tried with same image1=image2 -> I got T=[0,0,1]. What is the meaning of Tz=1? (where there is no z shift as both images are same)
And should I be aligning my keypoint coordinates with image center, or with principle focus obtained from calibration?
here is my code, I don't know how to solve it...
def count_neighbours(grid, row, col):
neighbor_rule = ((-1, -1), (-1, 0), (-1, 1), (0, -1),
(0, 1), (1, -1), (1, 0), (1, 1))
chip = 0
for each_loc in neighbor_rule:
n_row = row + each_loc[0]
n_col = col + each_loc[1]
if 0 <= n_row < len(gird) and 0 <= n_col < len(gird[0]):
if gird[n_row][n_col] == 1:
chip += 1
else:
continue
return chip
So I wanted to see if I could make fractal flames using matplotlib and figured a good test would be the sierpinski triangle. I modified a working version I had that simply performed the chaos game by normalizing the x range from -2, 2 to 0, 400 and the y range from 0, 2 to 0, 200. I also truncated the x and y coordinates to 2 decimal places and multiplied by 100 so that the coordinates could be put in to a matrix that I could apply a color map to. Here's the code I'm working on right now (please forgive the messiness):
import numpy as np
import matplotlib.pyplot as plt
import math
import random
def f(x, y, n):
N = np.array([[x, y]])
M = np.array([[1/2.0, 0], [0, 1/2.0]])
b = np.array([[.5], [0]])
b2 = np.array([[0], [.5]])
if n == 0:
return np.dot(M, N.T)
elif n == 1:
return np.dot(M, N.T) + 2*b
elif n == 2:
return np.dot(M, N.T) + 2*b2
elif n == 3:
return np.dot(M, N.T) - 2*b
def norm_x(n, minX_1, maxX_1, minX_2, maxX_2):
rng = maxX_1 - minX_1
n = (n - minX_1) / rng
rng_2 = maxX_2 - minX_2
n = (n * rng_2) + minX_2
return n
def norm_y(n, minY_1, maxY_1, minY_2, maxY_2):
rng = maxY_1 - minY_1
n = (n - minY_1) / rng
rng_2 = maxY_2 - minY_2
n = (n * rng_2) + minY_2
return n
# Plot ranges
x_min, x_max = -2.0, 2.0
y_min, y_max = 0, 2.0
# Even intervals for points to compute orbits of
x_range = np.arange(x_min, x_max, (x_max - x_min) / 400.0)
y_range = np.arange(y_min, y_max, (y_max - y_min) / 200.0)
mat = np.zeros((len(x_range) + 1, len(y_range) + 1))
random.seed()
x = 1
y = 1
for i in range(0, 100000):
n = random.randint(0, 3)
V = f(x, y, n)
x = V.item(0)
y = V.item(1)
mat[norm_x(x, -2, 2, 0, 400), norm_y(y, 0, 2, 0, 200)] += 50
plt.xlabel('x0')
plt.ylabel('y')
fig = plt.figure(figsize=(10,10))
plt.imshow(mat, cmap="spectral", extent=[-2,2, 0, 2])
plt.show()
The mathematics seem solid here so I suspect something weird is going on with how I'm handling where things should go into the 'mat' matrix and how the values in there correspond to the colormap.
If I understood your problem correctly, you need to transpose your matrix using the method .T. So just replace
fig = plt.figure(figsize=(10,10))
plt.imshow(mat, cmap="spectral", extent=[-2,2, 0, 2])
plt.show()
by
fig = plt.figure(figsize=(10,10))
ax = gca()
ax.imshow(mat.T, cmap="spectral", extent=[-2,2, 0, 2], origin="bottom")
plt.show()
The argument origin=bottom tells to imshow to have the origin of your matrix at the bottom of the figure.
Hope it helps.