Hi guys, I'm having a strange issue. I'm working on a project to identify lines in images and derive marker points along them. To this end I have a script that uses both the Canny and Sobel edge detection methods from openCV inside of Photoscan. The Canny detector works, but Sobel is not, seems to lock photoscan right off the bat.
If you want to try this out on your own computer you need to use PIP to instal numpy as well as openCV. I suggest downloading the openCV3.0 whl file with the contributors packages and using pip to install it into your Photoscan Python installation.
The full code is below, like I said, Canny is working, Sobel is not. You can find the code under the edge_detector() function. When I run the Sobel code by itself through the command prompt everything works just fine. I'm wondering if there is some sort of background task that prevents this in Photoscan.
import PhotoScan, cv2, numpy
def cross(a, b):
result = PhotoScan.Vector([a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y *b.x])
return result
def findIndex(in_photo):
global cam_num
cams = chunk.cameras
cam_index = 0
for item in cams:
if item.label == in_photo:
cam_num = cam_index
cam_index += 1
def run_script(points, img_name):
global chunk
photo_name = img_name.split('/')[-1]
chunk = PhotoScan.app.document.chunk # set the chunk
model = chunk.model
vertices = chunk.model.vertices
print(chunk)
if chunk.transform.matrix:
T0 = chunk.transform.matrix
else:
T0 = PhotoScan.Matrix().diag([1, 1, 1, 1])
for point in points:
x = point[0]
y = point[1]
marker_2D = (x, y) # pixel coordinates on the image for marker
findIndex(photo_name)
camera = chunk.cameras[cam_num] # sets the camera to create marker on
marker = chunk.addMarker() # creates a marker on the chunk
marker.projections[camera] = marker_2D # moves that marker to appropriate location on image
point_2D = marker.projections[camera].coord
vect = camera.sensor.calibration.unproject(point_2D)
vect = camera.transform.mulv(vect)
center = camera.center
# estimating ray and surface intersection
for face in model.faces:
v = face.vertices
E1 = PhotoScan.Vector(vertices[v[1]].coord - vertices[v[0]].coord)
E2 = PhotoScan.Vector(vertices[v[2]].coord - vertices[v[0]].coord)
D = PhotoScan.Vector(vect)
T = PhotoScan.Vector(center - vertices[v[0]].coord)
P = cross(D, E2)
Q = cross(T, E1)
result = PhotoScan.Vector([Q * E2, P * T, Q * D]) / (P * E1)
if (0 < result[1]) and (0 < result[2]) and (result[1] + result[2] <= 1):
t = (1 - result[1] - result[2]) * vertices[v[0]].coord
u = result[1] * vertices[v[1]].coord
v_ = result[2] * vertices[v[2]].coord
point_3D = T0.mulp(u + v_ + t)
point_3D = chunk.crs.project(point_3D)
break
point = chunk.crs.unproject(point_3D)
point = T0.inv().mulp(point)
for cur_camera in chunk.cameras:
if (cur_camera == camera) or not cur_camera.transform:
continue
cur_proj = cur_camera.project(point)
if (0 <= cur_proj[0] < camera.sensor.width) and (0 <= cur_proj[1] < camera.sensor.height):
marker.projections[cur_camera] = cur_proj
def edge_detector():
global image_name
detector_type = PhotoScan.app.getString('Canny or Sobel')
if detector_type.lower() == 'canny':
while True:
lower_canny = PhotoScan.app.getInt(label='Lower Canny Threshold', value=50)
upper_canny = PhotoScan.app.getInt(label='Upper Canny Threshold', value=150)
imge = cv2.imread(image_name, 0)
imgc = cv2.imread(image_name)
height, width, channels = imgc.shape
edges = cv2.Canny(imge, lower_canny, upper_canny)
try:
lines = cv2.HoughLinesP(edges, 1, numpy.pi / 180, 100, 100, 10)
line_image = numpy.zeros((height, width, 1), numpy.uint8) # create a blank image to draw lines on
for x in range(0, len(lines)):
for x1, y1, x2, y2 in lines[x]:
cv2.line(imgc, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.line(line_image, (x1, y1), (x2, y2), (255), 1)
break
except:
print('Try less restrictive thresholds')
break
elif detector_type.lower() == 'sobel':
while True:
sobel_thresh = PhotoScan.app.getFloat(label='Threshold value 0-255', value=100)
imge = cv2.imread(image_name, 0)
imgc = cv2.imread(image_name)
height, width, channels = imgc.shape
sobelx = cv2.Sobel(imge, cv2.CV_8U, 1, 0, ksize=3) # 3x3 kernel seems to work best
sobely = cv2.Sobel(imge, cv2.CV_8U, 0, 1, ksize=3)
edges = cv2.add(sobelx, sobely) # add the x and y sobel operators together to get all edges
# apply threshold to binary image. All pixels with value below threshold set to 0, all above set to 255
ret, thresh = cv2.threshold(edges, 100, 255, cv2.THRESH_BINARY)
lines = cv2.HoughLinesP(thresh, 1, numpy.pi / 180, 200, 200, 25)
line_image = numpy.zeros((height, width, 1), numpy.uint8) # create a blank image to draw lines on
for x in range(0, len(lines)):
for x1, y1, x2, y2 in lines[x]:
cv2.line(imgc, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.line(line_image, (x1, y1), (x2, y2), (255), 1)
else:
print('That is not a valid selection type')
return (line_image, imgc)
def find_nearest_white(IMG, TARGET):
nonzero = cv2.findNonZero(IMG)
distances = numpy.sqrt((nonzero[:, :, 0] - TARGET[0]) ** 2 + (nonzero[:, :, 1] - TARGET[1]) ** 2)
nearest_index = numpy.argmin(distances)
return nonzero[nearest_index]
def click(event, x, y, flags, param):
global target
global display_imgs
global white_point
global link_list
if event == cv2.EVENT_LBUTTONDOWN:
target = (x, y)
white_point = find_nearest_white(display_imgs[0], target)
find_connected(display_imgs[0], white_point)
for item in link_list:
cv2.circle(display_imgs[1], (item[0], item[1]), 3, (0, 0, 255), 1)
cv2.imshow('Image', display_imgs[1])
def find_connected(img, in_coords):
global link_list
in_x = int(in_coords[0][0])
in_y = int(in_coords[0][1])
x = 0
while x < 500:
iter_list = [(in_x + 1, in_y), (in_x + 1, in_y - 1), (in_x, in_y - 1), (in_x - 1, in_y - 1), (in_x - 1, in_y),
(in_x - 1, in_y + 1), (in_x, in_y + 1), (in_x + 1, in_y + 1)]
check_count = len(link_list)
for item in iter_list:
px = int(img[item[1], item[0]])
if px > 0:
in_x = item[0]
in_y = item[1]
link_list.append((in_x, in_y))
else:
pass
x += 1
return link_list
def image_processing():
global display_imgs
global link_list
display_imgs = edge_detector()
cv2.namedWindow('Image', cv2.WINDOW_NORMAL)
cv2.setMouseCallback('Image', click)
while True:
cv2.imshow('Image', display_imgs[1])
cv2.resizeWindow('Image', 1600, 1200)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
link_list = list(set(link_list)) # removes duplicates from list if they arise
print(link_list)
run_script(link_list, image_name)
link_list = []
target = ()
white_point = []
cam_num = 0
image_name = PhotoScan.app.getOpenFileName()
image_processing()