OSP RealPi2dDIC
RealPi2dDIC is a free and open source Digital Image Correlation software which generates in-situ two dimensional strain field of any structure under loading. This software while running captures images using a Picamera v2 module, registers the image data, correlates with a reference image using computer vision. This software is based on Lucas-Kanade Method, a well established technique for measurement of optical flow based upon image correlation. It returns the local strain field over a region of interest selected by the user in both x and y directions.
To know about the operational prcedures, please read the Standard Operating Procedure.
This software is developed at the Institute of Predictive Performance Methodologies (IPPM), Univerisity of Texas at Arlington Research Institute (UTARI), Texas, USA.
Authors: Partha Pratim Das [parthapratim.das@mavs.uta.edu] , Muthu Ram Prabhu Elenchezhian, Md Rassel Raihan [mdrassel.raihan@uta.edu], Vamsee Vadlamudi, Kenneth Reifsnider
This is a free software. It can be distributed/modified under the terms of MIT Licence. See the LICENSE.md file for more details.
Modules Dependencies: i. picamera ii. opencv-python iii. numpy iv. scipy v. matplotlib
Hardware Dependencies: i. Raspberry Pi + Raspbian OS ii. Picamera Module v2
Expand source code
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
RealPi2dDIC is a free and open source Digital Image Correlation software which generates in-situ two dimensional strain field of any structure under loading. This software while
running captures images using a Picamera v2 module, registers the image data, correlates with a reference image using computer vision. This software is based
on Lucas-Kanade Method, a well established technique for measurement of optical flow based upon image correlation. It returns the local strain field over a
region of interest selected by the user in both x and y directions.
To know about the operational prcedures, please read Readme.md
This software is developed at the Institute of Predictive Performance Methodologies (IPPM), Univerisity of Texas at Arlington
Research Institute (UTARI), Texas, USA.
Authors: Partha Pratim Das [parthapratim.das@mavs.uta.edu] , Muthu Ram Prabhu Elenchezhian, Md Rassel Raihan [mdrassel.raihan@uta.edu], Vamsee Vadlamudi, Kenneth Reifsnider
This is a free software. It can be distributed/modified under the terms of MIT Licence. See the LICENSE.md file for more details.
Modules Dependencies:
i. picamera
ii. opencv-python
iii. numpy
iv. scipy
v. matplotlib
Hardware Dependencies:
i. Raspberry Pi + Raspbian OS
ii. Picamera Module v2
'''
import time
import math
import glob
import copy
import os
import shutil
import keyboard
import numpy as np
import cv2
from matplotlib import pyplot as plt
import scipy.interpolate
import picamera
camera = picamera.PiCamera()
surface_grid = [] # points on surface are stored in this array
# correlation window size (in pixels) for Lucas Kanade Method for optical
# flow measurement # can be changed based on speckle pattern type (see documentation)
lk_window_size = (65, 65)
# the size in pixel of the interval (dx,dy) of the correlation grid
lk_grid_size = (10, 10)
area = []
cropping = False
class grid:
"""A class used for many important features starting from
capturing photos to image correlation and processing rawdata."""
def __init__(self, Points_X, Points_Y, size_x, size_y):
"""
This generates new grid object with x coordinate (Points_X)
y coordinate (Points_Y), number of point along x (size_x) and
number of point along y (size_y)"""
self.Points_X = Points_X
self.Points_Y = Points_Y
self.size_x = size_x
self.size_y = size_y
self.disp_x = self.Points_X.copy().fill(0.)
self.disp_y = self.Points_Y.copy().fill(0.)
self.strain_longitudinal = None
self.strain_transverse = None
self.strain_xy = None
def init_Raw_Data(self, winsize, reference_image, image, reference_point,
correlated_point, disp):
"""Saves raw image data using opencv to current grid object"""
self.winsize = winsize
self.reference_image = reference_image
self.image = image
self.reference_point = reference_point
self.correlated_point = correlated_point
self.disp = disp
def directory_Name(self, prefix, extension):
"""Makes specific directories for storing specific results including
surface image, grid displacement, raw data in CSV format"""
folder = os.path.dirname(self.image)
folder = folder + '/rawdata/' + prefix
if not os.path.exists(folder):
os.makedirs(folder)
base = os.path.basename(self.image)
name = folder + '/' + \
os.path.splitext(base)[0] + '_' + prefix + '.' + extension
return name
def save_Points(self, pref, poInt):
"""saves correlated points and reference points array to a txt file """
folderLocation = os.path.dirname(self.image)
folderLocation = folderLocation + '/rawdata/%s' % pref
if not os.path.exists(folderLocation):
os.makedirs(folderLocation)
num = os.path.splitext(os.path.basename(self.image))[0]
np.savetxt('%s/%s_%s.txt' % (folderLocation, pref, num),
poInt,
delimiter=',')
def correlation_Image(self):
"""Draws image correlation data points on reference image for each data point"""
name = self.directory_Name('marker', 'png')
display_Image(self.image,
point=self.correlated_point,
l_color=(0, 120, 255),
p_color=(140, 130, 0),
file_Name=name,
text=name)
self.save_Points('correlatedPoints', self.correlated_point)
def displacement_Image(self, scale):
"""Draws displacement image on reference image for each data point.
Parameters
----------
scale : int
to amplify the displacement
"""
name = self.directory_Name('disp', 'png')
display_Image(self.reference_image,
point=self.reference_point,
pointf=self.correlated_point,
l_color=(125, 0, 125),
p_color=(125, 125, 125),
scale=scale,
file_Name=name,
text=name)
self.save_Points('DisplacementPoints', self.reference_point)
def surface_Image(self, scale):
"""Draws mesh deformations on reference image for each data point.
Parameters
----------
scale : int
to amplify the deformation
"""
name = self.directory_Name('surface', 'png')
display_Image(self.reference_image,
grid=self,
scale=scale,
surfColor=(255, 0, 250),
file_Name=name,
text=name)
def raw_Data_CSV(self):
"""writes a csv file for displacement, strain and other parameters.
This data can be used for post processing"""
name = self.directory_Name('result', 'csv')
f = open(name, 'w')
f.write("index" + ',' + "index_x" + ',' + "index_y" + ',' + "x (px)" +
',' + "y (px)" + ',' + "x_Displacement" + ',' +
"y_Displacement" + ',' + "x_Strain" + ',' + "y_Strain" + ',' +
"xy_Strain" + '\n')
index = 0
for i in range(self.size_x):
for j in range(self.size_y):
f.write(
str(index) + ',' + str(i) + ',' + str(j) + ',' +
str(self.Points_X[i, j]) + ',' + str(self.Points_Y[i, j]) +
',' + str(self.disp_x[i, j]) + ',' +
str(self.disp_y[i, j]) + ',' + str(self.strain_longitudinal[i, j]) +
',' + str(self.strain_transverse[i, j]) + ',' +
str(self.strain_xy[i, j]) + '\n')
index = index + 1
f.close()
def insitu_Plot(self, field, title, fig_No, img_file_Name):
"""Connects with Plot Class. Plots in-situ strain field using matplotlib interactive mapping"""
img_folder = './Test_%s/*.jpg' % img_file_Name
img_listt = sorted(glob.glob(img_folder),
key=lambda t: os.stat(t).st_mtime)
image_ref = cv2.imread(img_listt[0], 0)
Plot(image_ref, self, field, title, fig_No)
def bivariate_Interpolation(self, point, disp, *args, **kwargs):
"""Interpolates the displacement field. Bivariate B-spline interpolation algorithm from scipy is used here.
for no interpolation option this can use raw method"""
dx = np.array([d[0] for d in disp])
dy = np.array([d[1] for d in disp])
method = 'raw' if 'method' not in kwargs else kwargs['method']
if method == 'raw':
self.disp_x = self.Points_X.copy()
self.disp_y = self.Points_Y.copy()
count = 0
for i in range(self.disp_x.shape[0]):
for j in range(self.disp_x.shape[1]):
self.disp_x[i, j] = dx[count]
self.disp_y[i, j] = dy[count]
count = count + 1
elif method == 'bivar_Spline':
tck_x = scipy.interpolate.bisplrep(self.Points_X,
self.Points_Y,
dx,
kx=5,
ky=5)
self.disp_x = scipy.interpolate.bisplev(self.Points_X[:, 0],
self.Points_Y[0, :], tck_x)
tck_y = scipy.interpolate.bisplrep(self.Points_X,
self.Points_Y,
dy,
kx=5,
ky=5)
self.disp_y = scipy.interpolate.bisplev(self.Points_X[:, 0],
self.Points_Y[0, :], tck_y)
def strain_Field_Compute(self):
"""Computes Green-Langragian strain field from interpolated displacement data using numpy"""
dx = self.Points_X[1][0] - self.Points_X[0][0]
dy = self.Points_Y[0][1] - self.Points_Y[0][0]
strain_longitudinal, strain_xy = np.gradient(
self.disp_x, dx, dy, edge_order=2)
strain_yx, strain_transverse = np.gradient(
self.disp_y, dx, dy, edge_order=2)
self.strain_longitudinal = strain_longitudinal + .5 * \
(np.power(strain_longitudinal, 2) + np.power(strain_xy, 2))
self.strain_transverse = strain_transverse + .5 * \
(np.power(strain_transverse, 2) + np.power(strain_yx, 2))
self.strain_xy = .5 * (strain_xy + strain_yx + strain_longitudinal * strain_xy +
strain_yx * strain_transverse)
class Plot:
""" Plot class includes all the required operations for interactive plotting of strain field data on the reference image
using matplotlib """
def __init__(self, image, grid, data, title, fig):
self.data = np.ma.masked_invalid(data)
self.data_copy = np.copy(self.data)
self.Points_X = grid.Points_X
self.Points_Y = grid.Points_Y
self.data = np.ma.array(self.data, mask=self.data == np.nan)
self.fig = plt.figure(fig)
plt.clf()
self.ax = self.fig.add_subplot(111)
self.fig.subplots_adjust(left=0.15, bottom=0.05, right=0.95, top=1.00)
self.ax.imshow(image, cmap='Greys')
self.im = self.ax.contourf(grid.Points_X,
grid.Points_Y,
self.data,
50,
cmap='rainbow',
alpha=0.75)
self.ax.set_title(title)
self.ax.set_ylabel('Height (Pixels)')
self.ax.set_xlabel('Width (Pixels)')
self.cb = self.fig.colorbar(self.im)
def surface_Generate(area, num_point, *args, **kwargs):
"""Generates a surface grid using points from region of interest"""
xmin = area[0][0]
xmax = area[1][0]
dx = xmax - xmin
ymin = area[0][1]
ymax = area[1][1]
dy = ymax - ymin
point_surface = dx * dy / num_point
point_line = math.sqrt(point_surface)
ratio = 1. if 'ratio' not in kwargs else kwargs['ratio']
num_x = int(ratio * dx / point_line) + 1
num_y = int(ratio * dy / point_line) + 1
Points_X, Points_Y = np.mgrid[xmin:xmax:num_x * 1j, ymin:ymax:num_y * 1j]
return grid(Points_X, Points_Y, num_x, num_y)
def display_Image(image, *args, **kwargs):
"""Draws opencv images for raw data visualization
Parameters
----------
- point : np.array
- p_color : (r,g,b) value
arg to choose the color of point
- pointf : np.array
draw lines between point and pointf
- l_color : (r,g,b) value
arg to choose the color of lines
- grid : to display a grid, the grid must be a grid object
- surfColor : to choose the grid color"""
if isinstance(image, str):
image = cv2.imread(image, 0)
if 'text' in kwargs:
text = kwargs['text']
image = cv2.putText(image, text, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1,
(255, 255, 255), 4)
frame = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
if 'point' in kwargs:
p_color = (160, 120,
0) if 'p_color' not in kwargs else kwargs['p_color']
for pt in kwargs['point']:
if not np.isnan(pt[0]) and not np.isnan(pt[1]):
x = int(pt[0])
y = int(pt[1])
frame = cv2.circle(frame, (x, y), 4, p_color, -1)
scale = 1. if 'scale' not in kwargs else kwargs['scale']
if 'pointf' in kwargs and 'point' in kwargs:
assert len(kwargs['point']) == len(kwargs['pointf']), 'bad size'
l_color = (255, 120,
255) if 'l_color' not in kwargs else kwargs['l_color']
for i, pt0 in enumerate(kwargs['point']):
pt1 = kwargs['pointf'][i]
if np.isnan(
pt0[0]) == False and np.isnan(
pt0[1]) == False and np.isnan(
pt1[0]) == False and np.isnan(
pt1[1]) == False:
disp_x = (pt1[0] - pt0[0]) * scale
disp_y = (pt1[1] - pt0[1]) * scale
frame = cv2.line(frame, (pt0[0], pt0[1]),
(int(pt0[0] + disp_x), int(pt0[1] + disp_y)),
l_color, 2)
if 'grid' in kwargs:
gr = kwargs['grid']
surfColor = (255, 255,
255) if 'surfColor' not in kwargs else kwargs['surfColor']
for i in range(gr.size_x):
for j in range(gr.size_y):
if (not math.isnan(gr.Points_X[i, j]) and
not math.isnan(gr.Points_Y[i, j]) and
not math.isnan(gr.disp_x[i, j]) and
not math.isnan(gr.disp_y[i, j])):
x = int(gr.Points_X[i, j]) + int(gr.disp_x[i, j] * scale)
y = int(gr.Points_Y[i, j]) + int(gr.disp_y[i, j] * scale)
if i < (gr.size_x - 1):
if (not math.isnan(gr.Points_X[i + 1, j]) and
not math.isnan(gr.Points_Y[i + 1, j]) and
not math.isnan(gr.disp_x[i + 1, j]) and
not math.isnan(gr.disp_y[i + 1, j])):
x1 = int(gr.Points_X[i + 1, j]) + \
int(gr.disp_x[i + 1, j] * scale)
y1 = int(gr.Points_Y[i + 1, j]) + \
int(gr.disp_y[i + 1, j] * scale)
frame = cv2.line(
frame, (x, y), (x1, y1), surfColor, 2)
if j < (gr.size_y - 1):
if (not math.isnan(gr.Points_X[i, j + 1]) and
not math.isnan(gr.Points_Y[i, j + 1]) and
not math.isnan(gr.disp_x[i, j + 1]) and
not math.isnan(gr.disp_y[i, j + 1])):
x1 = int(gr.Points_X[i, j + 1]) + \
int(gr.disp_x[i, j + 1] * scale)
y1 = int(gr.Points_Y[i, j + 1]) + \
int(gr.disp_y[i, j + 1] * scale)
frame = cv2.line(
frame, (x, y), (x1, y1), surfColor, 4)
if 'file_Name' in kwargs:
cv2.imwrite(kwargs['file_Name'], frame)
return
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.resizeWindow('image', frame.shape[1], frame.shape[0])
cv2.imshow('image', frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
def write_img_data(raw_Image_Data_File, image, points):
"""Writes image data points to the designated txt file."""
raw_Image_Data_File.write(image + '\t')
for p in points:
raw_Image_Data_File.write(str(p[0]) + ',' + str(p[1]) + '\t')
raw_Image_Data_File.write('\n')
def capture_Parameters():
"""UI to check Brightness, Contrast, ISO and Exposure compensation of picamera and define values"""
def nothing(x):
pass
cv2.namedWindow('Press_C_to_Confirm')
cv2.createTrackbar('Brightness', 'Press_C_to_Confirm', 50, 100, nothing)
cv2.createTrackbar('Contrast', 'Press_C_to_Confirm', 50, 100, nothing)
cv2.createTrackbar('Exposure', 'Press_C_to_Confirm', 25, 50, nothing)
cv2.createTrackbar('ISO', 'Press_C_to_Confirm', 1, 1600, nothing)
camera.exposure_mode = 'beach' # Can be changed. Follow picamera documentation
camera.awb_mode = 'tungsten' # Can be changed. Follow picamera documentation
camera.start_preview(fullscreen=False, window=(600, 400, 640, 480))
while True:
brightness = cv2.getTrackbarPos("Brightness", "Press_C_to_Confirm")
camera.brightness = brightness
contrast = cv2.getTrackbarPos("Contrast", "Press_C_to_Confirm")
camera.contrast = contrast
expos = cv2.getTrackbarPos("Exposure", "Press_C_to_Confirm")
expos = int(expos - 25)
camera.exposure_compensation = expos
iso = cv2.getTrackbarPos("ISO", "Press_C_to_Confirm")
camera.iso = iso
k = cv2.waitKey(1) & 0xFF
if k == ord('c'):
camera.stop_preview()
cv2.destroyAllWindows()
break
# This can be changed based on the processing power and speed of the device
# For maximum resolution, check picamera documentation
camera.resolution = (1920, 1080)
def displacement_Compute(point, pointf):
"""Computes displacement between two image point arrays"""
assert len(point) == len(pointf)
values = []
for i, pt0 in enumerate(point):
pt1 = pointf[i]
values.append((pt1[0] - pt0[0], pt1[1] - pt0[1]))
return values
def select_ROI(captured_Img):
"""UI to select the Region of Interest on the 2D surface"""
global area, cropping
image = cv2.putText(
captured_Img,
"Choose Region of Interest (ROI) | Press c to Confirm, r to Reset",
(50, 1000), cv2.FONT_HERSHEY_DUPLEX, 1, (255, 0, 255), 2)
def select_Point(event, x, y, flags, param):
global area, cropping
if event == cv2.EVENT_LBUTTONDOWN:
area = [(x, y)]
cropping = True
elif event == cv2.EVENT_LBUTTONUP:
area.append((x, y))
cropping = False
draw_Image = cv2.rectangle(
image, area[0], area[1], (125, 115, 65), 2)
cv2.imshow('image', draw_Image)
clone = image.copy()
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.resizeWindow('image', 640, 480)
cv2.setMouseCallback("image", select_Point)
while True:
cv2.imshow("image", image)
key = cv2.waitKey(1) & 0xFF
if key == ord("r"):
image = clone.copy()
elif key == ord("c"):
break
return area
def list_Point_Final(points, area, *args, **kwargs):
"""An internal function to organize the image points for further calculation"""
xmin = area[0][0]
xmax = area[1][0]
ymin = area[0][1]
ymax = area[1][1]
res = []
for p in points:
x = p[0]
y = p[1]
if ((x >= xmin) and (x <= xmax) and (y >= ymin) and (y <= ymax)):
res.append(p)
return np.array(res)
def displacement_Measure(p1, p2):
"""A supporting function for displacement_Compute to measure displacement"""
A = []
B = []
removed_indices = []
for i in range(len(p1)):
if np.isnan(p1[i][0]):
assert np.isnan(p1[i][0]) and np.isnan(p1[i][1]) and np.isnan(
p2[i][0]) and np.isnan(p2[i][1])
removed_indices.append(i)
else:
A.append(p1[i])
B.append(p2[i])
A = np.matrix(A)
B = np.matrix(B)
assert len(A) == len(B)
N = A.shape[0]
centroid_A = np.mean(A, axis=0)
centroid_B = np.mean(B, axis=0)
AA = np.matrix(A - np.tile(centroid_A, (N, 1)))
BB = np.matrix(B - np.tile(centroid_B, (N, 1)))
H = np.transpose(AA) * BB
U, S, Vt = np.linalg.svd(H)
R = Vt.T * U.T
n = len(A)
T = -R * centroid_A.T + centroid_B.T
A2 = (R * A.T) + np.tile(T, (1, n))
A2 = np.array(A2.T)
out = []
j = 0
for i in range(len(p1)):
if np.isnan(p1[i][0]):
out.append(p1[i])
else:
out.append(A2[j])
j = j + 1
out = np.array(out)
return displacement_Compute(p2, out)
def raw_Data_Process(raw_Image_Data_File, number, *args, **kwargs):
"""the raw_Data_Process is a a wrapper function that reads the raw result txt file, calculates displacements and strain fields
and generate interpolated/non-interpolated data for using in Plot Class. This also generates raw outputs if user prompts.
Parameters
----------
- 'raw_Image_Data_File' : Path to the file of the result txt file where raw image data is stored.
- 'number' : iteration step value, acquired from the DIC_Run function
* Other Arguments:
- 'interpolation' the allowed vals are 'raw', 'bivar_Spline'
raw : No Interpolation
bivar_Spline : interpolates displacement data using bivariate spline interpolation
- 'export_RAW' : True or False ; True to get RAW displacement, strain and other important outputs for post processing.
- 'scale_disp' : float; used to amplify the displacement values on displacement output images
- 'scale_mesh' : float; used to amplify the pointwise mesh displacement values on mesh output images
"""
interpolation = 'raw' if 'interpolation' not in kwargs else kwargs[
'interpolation']
export_RAW = True if 'export_RAW' not in kwargs else kwargs['export_RAW']
scale_disp = 1. if 'scale_disp' not in kwargs else float(
kwargs['scale_disp'])
scale_mesh = 1. if 'scale_mesh' not in kwargs else float(
kwargs['scale_mesh'])
with open(raw_Image_Data_File) as f:
head = f.readlines()[0:2]
(xmin, xmax, xnum, window__Size_x) = [float(x) for x in head[0].split()]
(ymin, ymax, ynum, window__Size_y) = [float(x) for x in head[1].split()]
window__Size = (window__Size_x, window__Size_y)
Points_X, Points_Y = np.mgrid[xmin:xmax:int(xnum) * 1j,
ymin:ymax:int(ynum) * 1j]
surface_Maps = grid(Points_X, Points_Y, int(xnum), int(ynum))
list_Points_arr = []
list_Image_arr = []
list_Displacement_arr = []
with open(raw_Image_Data_File) as f:
res = f.readlines()[2:-1]
for line in res:
val = line.split('\t')
list_Image_arr.append(val[0])
point = []
for pair in val[1:-1]:
(x, y) = [float(x) for x in pair.split(',')]
point.append(np.array([np.float32(x), np.float32(y)]))
list_Points_arr.append(np.array(point))
surface_grid.append(copy.deepcopy(surface_Maps))
f.close()
k = number
for p, surface_Maps in enumerate(surface_grid):
print('.') # for registering values of k, do not comment
print("computing strain field of", list_Image_arr[k], "...")
disp = displacement_Measure(list_Points_arr[k], list_Points_arr[0])
surface_Maps.init_Raw_Data(
window__Size,
list_Image_arr[0],
list_Image_arr[k],
list_Points_arr[0],
list_Points_arr[k],
disp)
list_Displacement_arr.append(disp)
surface_Maps.bivariate_Interpolation(
list_Points_arr[0], disp, method=interpolation)
surface_Maps.strain_Field_Compute()
if (export_RAW):
surface_Maps.correlation_Image()
surface_Maps.displacement_Image(scale_disp)
surface_Maps.surface_Image(scale_mesh)
surface_Maps.raw_Data_CSV()
def DIC_Run(image_Path, file_Name, window_Size_px, mesh_size_px,
raw_Image_Data_File, *args, **kwargs):
"""
This DIC_Run function is a simple wrapper function that captures images in a specific time interval.
Region of interest is selected by the user and displacements are computed with each increment. A result txt file
is written for image correlation.
- 'image_Path' : the path where images will be captured
- 'window_Size_px' : size in pixel of your correlation windows for LK Method
- 'mesh_size_px' : the size of your correlation grid or mesh. The smaller the better, but requires more processing
power
- 'raw_Image_Data_File' : locates your result txt file
"""
capture_Parameters()
camera.start_preview()
time.sleep(1)
file_location = './Test_%s/' % file_Name
shutil.rmtree(file_location)
if not os.path.exists(file_location):
os.mkdir(file_location)
camera.capture(file_location + '0.jpg')
camera.stop_preview()
img_ref = cv2.imread(file_location + '0.jpg', 0)
img_list = sorted(glob.glob(image_Path), key=lambda t: os.stat(t).st_mtime)
area = select_ROI(img_ref)
points = []
points_x = np.float64(np.arange(area[0][0], area[1][0], mesh_size_px[0]))
points_y = np.float64(np.arange(area[0][1], area[1][1], mesh_size_px[1]))
for x in points_x:
for y in points_y:
points.append(np.array([np.float32(x), np.float32(y)]))
points = np.array(points)
point_List_Final = list_Point_Final(points, area, shape='box')
img_ref = cv2.imread(img_list[0], 0)
img_ref = cv2.putText(img_ref, "Press any button to continue", (50, 1000),
cv2.FONT_HERSHEY_DUPLEX, 1, (255, 255, 255), 2)
display_Image(img_ref, point=point_List_Final)
f = open(raw_Image_Data_File, 'w')
xmin = points_x[0]
xmax = points_x[-1]
xnum = len(points_x)
ymin = points_y[0]
ymax = points_y[-1]
ynum = len(points_y)
f.write(
str(xmin) + '\t' + str(xmax) + '\t' + str(int(xnum)) + '\t' +
str(int(window_Size_px[0])) + '\n')
f.write(
str(ymin) + '\t' + str(ymax) + '\t' + str(int(ynum)) + '\t' +
str(int(window_Size_px[1])) + '\n')
# Lucas-Kanade Method Paramaters from OpenCV
lk_params = dict(winSize=window_Size_px,
maxLevel=10,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,
10, 0.03))
ref_Points = point_List_Final
write_img_data(f, img_list[0], ref_Points)
f.close()
i = 0
plt.ion()
while True:
f = open(raw_Image_Data_File, 'a')
img_list = sorted(glob.glob(image_Path),
key=lambda t: os.stat(t).st_mtime)
image_ref = cv2.imread(img_list[i], 0)
camera.capture(file_location + '%i.jpg' % (i + 1))
img_list = sorted(glob.glob(image_Path),
key=lambda t: os.stat(t).st_mtime)
image_str = cv2.imread(img_list[i + 1], 0)
ref_Points, st, err = cv2.calcOpticalFlowPyrLK(image_ref, image_str,
ref_Points, None,
**lk_params)
write_img_data(f, img_list[i + 1], ref_Points)
f.close()
img_list = sorted(glob.glob(image_Path),
key=lambda t: os.stat(t).st_mtime)
raw_Data_Process('./rawdata_%s.txt' % file_Name,
i,
interpolation='bivar_Spline',
export_RAW=False)
surfaceArr = surface_grid[-1]
os.mkdir(file_location + 'fig_%s' % (i))
surfaceArr.insitu_Plot(surfaceArr.strain_longitudinal, 'x strain', 1,
file_Name)
plt.savefig(file_location + 'fig_%s/x_strain_%s.png' % (i, i), dpi=300)
surfaceArr.insitu_Plot(
surfaceArr.strain_transverse,
'y strain',
2,
file_Name)
plt.savefig(file_location + 'fig_%s/y_strain_%s.png' % (i, i), dpi=300)
print("Saved Strain Field for Image_%s..." % i)
plt.show(block=False)
plt.pause(1)
i = i + 1
f.write('\n')
f.close()
""" This part is executed and user information is given here. """
while True:
file_Name = input("Folder Name: ")
if os.path.exists('./Test_%s' % file_Name):
print('Folder exists already\nwarning: Data will be overwritten \nChoose different name')
continue
else:
os.mkdir('./Test_%s' % file_Name)
break
DIC_Run('./Test_%s/*.jpg' % file_Name, file_Name,
lk_window_size, lk_grid_size, "rawdata_%s.txt" % file_Name)
Functions
def DIC_Run(image_Path, file_Name, window_Size_px, mesh_size_px, raw_Image_Data_File, *args, **kwargs)
-
This DIC_Run function is a simple wrapper function that captures images in a specific time interval. Region of interest is selected by the user and displacements are computed with each increment. A result txt file is written for image correlation.
- 'image_Path' : the path where images will be captured
- 'window_Size_px' : size in pixel of your correlation windows for LK Method
- 'mesh_size_px' : the size of your correlation grid or mesh. The smaller the better, but requires more processing power
- 'raw_Image_Data_File' : locates your result txt file
Expand source code
def DIC_Run(image_Path, file_Name, window_Size_px, mesh_size_px, raw_Image_Data_File, *args, **kwargs): """ This DIC_Run function is a simple wrapper function that captures images in a specific time interval. Region of interest is selected by the user and displacements are computed with each increment. A result txt file is written for image correlation. - 'image_Path' : the path where images will be captured - 'window_Size_px' : size in pixel of your correlation windows for LK Method - 'mesh_size_px' : the size of your correlation grid or mesh. The smaller the better, but requires more processing power - 'raw_Image_Data_File' : locates your result txt file """ capture_Parameters() camera.start_preview() time.sleep(1) file_location = './Test_%s/' % file_Name shutil.rmtree(file_location) if not os.path.exists(file_location): os.mkdir(file_location) camera.capture(file_location + '0.jpg') camera.stop_preview() img_ref = cv2.imread(file_location + '0.jpg', 0) img_list = sorted(glob.glob(image_Path), key=lambda t: os.stat(t).st_mtime) area = select_ROI(img_ref) points = [] points_x = np.float64(np.arange(area[0][0], area[1][0], mesh_size_px[0])) points_y = np.float64(np.arange(area[0][1], area[1][1], mesh_size_px[1])) for x in points_x: for y in points_y: points.append(np.array([np.float32(x), np.float32(y)])) points = np.array(points) point_List_Final = list_Point_Final(points, area, shape='box') img_ref = cv2.imread(img_list[0], 0) img_ref = cv2.putText(img_ref, "Press any button to continue", (50, 1000), cv2.FONT_HERSHEY_DUPLEX, 1, (255, 255, 255), 2) display_Image(img_ref, point=point_List_Final) f = open(raw_Image_Data_File, 'w') xmin = points_x[0] xmax = points_x[-1] xnum = len(points_x) ymin = points_y[0] ymax = points_y[-1] ynum = len(points_y) f.write( str(xmin) + '\t' + str(xmax) + '\t' + str(int(xnum)) + '\t' + str(int(window_Size_px[0])) + '\n') f.write( str(ymin) + '\t' + str(ymax) + '\t' + str(int(ynum)) + '\t' + str(int(window_Size_px[1])) + '\n') # Lucas-Kanade Method Paramaters from OpenCV lk_params = dict(winSize=window_Size_px, maxLevel=10, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) ref_Points = point_List_Final write_img_data(f, img_list[0], ref_Points) f.close() i = 0 plt.ion() while True: f = open(raw_Image_Data_File, 'a') img_list = sorted(glob.glob(image_Path), key=lambda t: os.stat(t).st_mtime) image_ref = cv2.imread(img_list[i], 0) camera.capture(file_location + '%i.jpg' % (i + 1)) img_list = sorted(glob.glob(image_Path), key=lambda t: os.stat(t).st_mtime) image_str = cv2.imread(img_list[i + 1], 0) ref_Points, st, err = cv2.calcOpticalFlowPyrLK(image_ref, image_str, ref_Points, None, **lk_params) write_img_data(f, img_list[i + 1], ref_Points) f.close() img_list = sorted(glob.glob(image_Path), key=lambda t: os.stat(t).st_mtime) raw_Data_Process('./rawdata_%s.txt' % file_Name, i, interpolation='bivar_Spline', export_RAW=False) surfaceArr = surface_grid[-1] os.mkdir(file_location + 'fig_%s' % (i)) surfaceArr.insitu_Plot(surfaceArr.strain_longitudinal, 'x strain', 1, file_Name) plt.savefig(file_location + 'fig_%s/x_strain_%s.png' % (i, i), dpi=300) surfaceArr.insitu_Plot( surfaceArr.strain_transverse, 'y strain', 2, file_Name) plt.savefig(file_location + 'fig_%s/y_strain_%s.png' % (i, i), dpi=300) print("Saved Strain Field for Image_%s..." % i) plt.show(block=False) plt.pause(1) i = i + 1 f.write('\n') f.close()
def capture_Parameters()
-
UI to check Brightness, Contrast, ISO and Exposure compensation of picamera and define values
Expand source code
def capture_Parameters(): """UI to check Brightness, Contrast, ISO and Exposure compensation of picamera and define values""" def nothing(x): pass cv2.namedWindow('Press_C_to_Confirm') cv2.createTrackbar('Brightness', 'Press_C_to_Confirm', 50, 100, nothing) cv2.createTrackbar('Contrast', 'Press_C_to_Confirm', 50, 100, nothing) cv2.createTrackbar('Exposure', 'Press_C_to_Confirm', 25, 50, nothing) cv2.createTrackbar('ISO', 'Press_C_to_Confirm', 1, 1600, nothing) camera.exposure_mode = 'beach' # Can be changed. Follow picamera documentation camera.awb_mode = 'tungsten' # Can be changed. Follow picamera documentation camera.start_preview(fullscreen=False, window=(600, 400, 640, 480)) while True: brightness = cv2.getTrackbarPos("Brightness", "Press_C_to_Confirm") camera.brightness = brightness contrast = cv2.getTrackbarPos("Contrast", "Press_C_to_Confirm") camera.contrast = contrast expos = cv2.getTrackbarPos("Exposure", "Press_C_to_Confirm") expos = int(expos - 25) camera.exposure_compensation = expos iso = cv2.getTrackbarPos("ISO", "Press_C_to_Confirm") camera.iso = iso k = cv2.waitKey(1) & 0xFF if k == ord('c'): camera.stop_preview() cv2.destroyAllWindows() break # This can be changed based on the processing power and speed of the device # For maximum resolution, check picamera documentation camera.resolution = (1920, 1080)
def displacement_Compute(point, pointf)
-
Computes displacement between two image point arrays
Expand source code
def displacement_Compute(point, pointf): """Computes displacement between two image point arrays""" assert len(point) == len(pointf) values = [] for i, pt0 in enumerate(point): pt1 = pointf[i] values.append((pt1[0] - pt0[0], pt1[1] - pt0[1])) return values
def displacement_Measure(p1, p2)
-
A supporting function for displacement_Compute to measure displacement
Expand source code
def displacement_Measure(p1, p2): """A supporting function for displacement_Compute to measure displacement""" A = [] B = [] removed_indices = [] for i in range(len(p1)): if np.isnan(p1[i][0]): assert np.isnan(p1[i][0]) and np.isnan(p1[i][1]) and np.isnan( p2[i][0]) and np.isnan(p2[i][1]) removed_indices.append(i) else: A.append(p1[i]) B.append(p2[i]) A = np.matrix(A) B = np.matrix(B) assert len(A) == len(B) N = A.shape[0] centroid_A = np.mean(A, axis=0) centroid_B = np.mean(B, axis=0) AA = np.matrix(A - np.tile(centroid_A, (N, 1))) BB = np.matrix(B - np.tile(centroid_B, (N, 1))) H = np.transpose(AA) * BB U, S, Vt = np.linalg.svd(H) R = Vt.T * U.T n = len(A) T = -R * centroid_A.T + centroid_B.T A2 = (R * A.T) + np.tile(T, (1, n)) A2 = np.array(A2.T) out = [] j = 0 for i in range(len(p1)): if np.isnan(p1[i][0]): out.append(p1[i]) else: out.append(A2[j]) j = j + 1 out = np.array(out) return displacement_Compute(p2, out)
def display_Image(image, *args, **kwargs)
-
Draws opencv images for raw data visualization Parameters
- point : np.array
- p_color : (r,g,b) value arg to choose the color of point
- pointf : np.array draw lines between point and pointf
- l_color : (r,g,b) value arg to choose the color of lines
- grid : to display a grid, the grid must be a grid object
- surfColor : to choose the grid color
Expand source code
def display_Image(image, *args, **kwargs): """Draws opencv images for raw data visualization Parameters ---------- - point : np.array - p_color : (r,g,b) value arg to choose the color of point - pointf : np.array draw lines between point and pointf - l_color : (r,g,b) value arg to choose the color of lines - grid : to display a grid, the grid must be a grid object - surfColor : to choose the grid color""" if isinstance(image, str): image = cv2.imread(image, 0) if 'text' in kwargs: text = kwargs['text'] image = cv2.putText(image, text, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 4) frame = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) if 'point' in kwargs: p_color = (160, 120, 0) if 'p_color' not in kwargs else kwargs['p_color'] for pt in kwargs['point']: if not np.isnan(pt[0]) and not np.isnan(pt[1]): x = int(pt[0]) y = int(pt[1]) frame = cv2.circle(frame, (x, y), 4, p_color, -1) scale = 1. if 'scale' not in kwargs else kwargs['scale'] if 'pointf' in kwargs and 'point' in kwargs: assert len(kwargs['point']) == len(kwargs['pointf']), 'bad size' l_color = (255, 120, 255) if 'l_color' not in kwargs else kwargs['l_color'] for i, pt0 in enumerate(kwargs['point']): pt1 = kwargs['pointf'][i] if np.isnan( pt0[0]) == False and np.isnan( pt0[1]) == False and np.isnan( pt1[0]) == False and np.isnan( pt1[1]) == False: disp_x = (pt1[0] - pt0[0]) * scale disp_y = (pt1[1] - pt0[1]) * scale frame = cv2.line(frame, (pt0[0], pt0[1]), (int(pt0[0] + disp_x), int(pt0[1] + disp_y)), l_color, 2) if 'grid' in kwargs: gr = kwargs['grid'] surfColor = (255, 255, 255) if 'surfColor' not in kwargs else kwargs['surfColor'] for i in range(gr.size_x): for j in range(gr.size_y): if (not math.isnan(gr.Points_X[i, j]) and not math.isnan(gr.Points_Y[i, j]) and not math.isnan(gr.disp_x[i, j]) and not math.isnan(gr.disp_y[i, j])): x = int(gr.Points_X[i, j]) + int(gr.disp_x[i, j] * scale) y = int(gr.Points_Y[i, j]) + int(gr.disp_y[i, j] * scale) if i < (gr.size_x - 1): if (not math.isnan(gr.Points_X[i + 1, j]) and not math.isnan(gr.Points_Y[i + 1, j]) and not math.isnan(gr.disp_x[i + 1, j]) and not math.isnan(gr.disp_y[i + 1, j])): x1 = int(gr.Points_X[i + 1, j]) + \ int(gr.disp_x[i + 1, j] * scale) y1 = int(gr.Points_Y[i + 1, j]) + \ int(gr.disp_y[i + 1, j] * scale) frame = cv2.line( frame, (x, y), (x1, y1), surfColor, 2) if j < (gr.size_y - 1): if (not math.isnan(gr.Points_X[i, j + 1]) and not math.isnan(gr.Points_Y[i, j + 1]) and not math.isnan(gr.disp_x[i, j + 1]) and not math.isnan(gr.disp_y[i, j + 1])): x1 = int(gr.Points_X[i, j + 1]) + \ int(gr.disp_x[i, j + 1] * scale) y1 = int(gr.Points_Y[i, j + 1]) + \ int(gr.disp_y[i, j + 1] * scale) frame = cv2.line( frame, (x, y), (x1, y1), surfColor, 4) if 'file_Name' in kwargs: cv2.imwrite(kwargs['file_Name'], frame) return cv2.namedWindow('image', cv2.WINDOW_NORMAL) cv2.resizeWindow('image', frame.shape[1], frame.shape[0]) cv2.imshow('image', frame) cv2.waitKey(0) cv2.destroyAllWindows()
def list_Point_Final(points, area, *args, **kwargs)
-
An internal function to organize the image points for further calculation
Expand source code
def list_Point_Final(points, area, *args, **kwargs): """An internal function to organize the image points for further calculation""" xmin = area[0][0] xmax = area[1][0] ymin = area[0][1] ymax = area[1][1] res = [] for p in points: x = p[0] y = p[1] if ((x >= xmin) and (x <= xmax) and (y >= ymin) and (y <= ymax)): res.append(p) return np.array(res)
def raw_Data_Process(raw_Image_Data_File, number, *args, **kwargs)
-
the raw_Data_Process is a a wrapper function that reads the raw result txt file, calculates displacements and strain fields and generate interpolated/non-interpolated data for using in Plot Class. This also generates raw outputs if user prompts. Parameters
- 'raw_Image_Data_File' : Path to the file of the result txt file where raw image data is stored.
- 'number' : iteration step value, acquired from the DIC_Run function
- Other Arguments:
- 'interpolation' the allowed vals are 'raw', 'bivar_Spline' raw : No Interpolation bivar_Spline : interpolates displacement data using bivariate spline interpolation
- 'export_RAW' : True or False ; True to get RAW displacement, strain and other important outputs for post processing.
- 'scale_disp' : float; used to amplify the displacement values on displacement output images
- 'scale_mesh' : float; used to amplify the pointwise mesh displacement values on mesh output images
Expand source code
def raw_Data_Process(raw_Image_Data_File, number, *args, **kwargs): """the raw_Data_Process is a a wrapper function that reads the raw result txt file, calculates displacements and strain fields and generate interpolated/non-interpolated data for using in Plot Class. This also generates raw outputs if user prompts. Parameters ---------- - 'raw_Image_Data_File' : Path to the file of the result txt file where raw image data is stored. - 'number' : iteration step value, acquired from the DIC_Run function * Other Arguments: - 'interpolation' the allowed vals are 'raw', 'bivar_Spline' raw : No Interpolation bivar_Spline : interpolates displacement data using bivariate spline interpolation - 'export_RAW' : True or False ; True to get RAW displacement, strain and other important outputs for post processing. - 'scale_disp' : float; used to amplify the displacement values on displacement output images - 'scale_mesh' : float; used to amplify the pointwise mesh displacement values on mesh output images """ interpolation = 'raw' if 'interpolation' not in kwargs else kwargs[ 'interpolation'] export_RAW = True if 'export_RAW' not in kwargs else kwargs['export_RAW'] scale_disp = 1. if 'scale_disp' not in kwargs else float( kwargs['scale_disp']) scale_mesh = 1. if 'scale_mesh' not in kwargs else float( kwargs['scale_mesh']) with open(raw_Image_Data_File) as f: head = f.readlines()[0:2] (xmin, xmax, xnum, window__Size_x) = [float(x) for x in head[0].split()] (ymin, ymax, ynum, window__Size_y) = [float(x) for x in head[1].split()] window__Size = (window__Size_x, window__Size_y) Points_X, Points_Y = np.mgrid[xmin:xmax:int(xnum) * 1j, ymin:ymax:int(ynum) * 1j] surface_Maps = grid(Points_X, Points_Y, int(xnum), int(ynum)) list_Points_arr = [] list_Image_arr = [] list_Displacement_arr = [] with open(raw_Image_Data_File) as f: res = f.readlines()[2:-1] for line in res: val = line.split('\t') list_Image_arr.append(val[0]) point = [] for pair in val[1:-1]: (x, y) = [float(x) for x in pair.split(',')] point.append(np.array([np.float32(x), np.float32(y)])) list_Points_arr.append(np.array(point)) surface_grid.append(copy.deepcopy(surface_Maps)) f.close() k = number for p, surface_Maps in enumerate(surface_grid): print('.') # for registering values of k, do not comment print("computing strain field of", list_Image_arr[k], "...") disp = displacement_Measure(list_Points_arr[k], list_Points_arr[0]) surface_Maps.init_Raw_Data( window__Size, list_Image_arr[0], list_Image_arr[k], list_Points_arr[0], list_Points_arr[k], disp) list_Displacement_arr.append(disp) surface_Maps.bivariate_Interpolation( list_Points_arr[0], disp, method=interpolation) surface_Maps.strain_Field_Compute() if (export_RAW): surface_Maps.correlation_Image() surface_Maps.displacement_Image(scale_disp) surface_Maps.surface_Image(scale_mesh) surface_Maps.raw_Data_CSV()
def select_ROI(captured_Img)
-
UI to select the Region of Interest on the 2D surface
Expand source code
def select_ROI(captured_Img): """UI to select the Region of Interest on the 2D surface""" global area, cropping image = cv2.putText( captured_Img, "Choose Region of Interest (ROI) | Press c to Confirm, r to Reset", (50, 1000), cv2.FONT_HERSHEY_DUPLEX, 1, (255, 0, 255), 2) def select_Point(event, x, y, flags, param): global area, cropping if event == cv2.EVENT_LBUTTONDOWN: area = [(x, y)] cropping = True elif event == cv2.EVENT_LBUTTONUP: area.append((x, y)) cropping = False draw_Image = cv2.rectangle( image, area[0], area[1], (125, 115, 65), 2) cv2.imshow('image', draw_Image) clone = image.copy() cv2.namedWindow('image', cv2.WINDOW_NORMAL) cv2.resizeWindow('image', 640, 480) cv2.setMouseCallback("image", select_Point) while True: cv2.imshow("image", image) key = cv2.waitKey(1) & 0xFF if key == ord("r"): image = clone.copy() elif key == ord("c"): break return area
def surface_Generate(area, num_point, *args, **kwargs)
-
Generates a surface grid using points from region of interest
Expand source code
def surface_Generate(area, num_point, *args, **kwargs): """Generates a surface grid using points from region of interest""" xmin = area[0][0] xmax = area[1][0] dx = xmax - xmin ymin = area[0][1] ymax = area[1][1] dy = ymax - ymin point_surface = dx * dy / num_point point_line = math.sqrt(point_surface) ratio = 1. if 'ratio' not in kwargs else kwargs['ratio'] num_x = int(ratio * dx / point_line) + 1 num_y = int(ratio * dy / point_line) + 1 Points_X, Points_Y = np.mgrid[xmin:xmax:num_x * 1j, ymin:ymax:num_y * 1j] return grid(Points_X, Points_Y, num_x, num_y)
def write_img_data(raw_Image_Data_File, image, points)
-
Writes image data points to the designated txt file.
Expand source code
def write_img_data(raw_Image_Data_File, image, points): """Writes image data points to the designated txt file.""" raw_Image_Data_File.write(image + '\t') for p in points: raw_Image_Data_File.write(str(p[0]) + ',' + str(p[1]) + '\t') raw_Image_Data_File.write('\n')
Classes
class Plot (image, grid, data, title, fig)
-
Plot class includes all the required operations for interactive plotting of strain field data on the reference image using matplotlib
Expand source code
class Plot: """ Plot class includes all the required operations for interactive plotting of strain field data on the reference image using matplotlib """ def __init__(self, image, grid, data, title, fig): self.data = np.ma.masked_invalid(data) self.data_copy = np.copy(self.data) self.Points_X = grid.Points_X self.Points_Y = grid.Points_Y self.data = np.ma.array(self.data, mask=self.data == np.nan) self.fig = plt.figure(fig) plt.clf() self.ax = self.fig.add_subplot(111) self.fig.subplots_adjust(left=0.15, bottom=0.05, right=0.95, top=1.00) self.ax.imshow(image, cmap='Greys') self.im = self.ax.contourf(grid.Points_X, grid.Points_Y, self.data, 50, cmap='rainbow', alpha=0.75) self.ax.set_title(title) self.ax.set_ylabel('Height (Pixels)') self.ax.set_xlabel('Width (Pixels)') self.cb = self.fig.colorbar(self.im)
class grid (Points_X, Points_Y, size_x, size_y)
-
A class used for many important features starting from capturing photos to image correlation and processing rawdata.
This generates new grid object with x coordinate (Points_X) y coordinate (Points_Y), number of point along x (size_x) and number of point along y (size_y)
Expand source code
class grid: """A class used for many important features starting from capturing photos to image correlation and processing rawdata.""" def __init__(self, Points_X, Points_Y, size_x, size_y): """ This generates new grid object with x coordinate (Points_X) y coordinate (Points_Y), number of point along x (size_x) and number of point along y (size_y)""" self.Points_X = Points_X self.Points_Y = Points_Y self.size_x = size_x self.size_y = size_y self.disp_x = self.Points_X.copy().fill(0.) self.disp_y = self.Points_Y.copy().fill(0.) self.strain_longitudinal = None self.strain_transverse = None self.strain_xy = None def init_Raw_Data(self, winsize, reference_image, image, reference_point, correlated_point, disp): """Saves raw image data using opencv to current grid object""" self.winsize = winsize self.reference_image = reference_image self.image = image self.reference_point = reference_point self.correlated_point = correlated_point self.disp = disp def directory_Name(self, prefix, extension): """Makes specific directories for storing specific results including surface image, grid displacement, raw data in CSV format""" folder = os.path.dirname(self.image) folder = folder + '/rawdata/' + prefix if not os.path.exists(folder): os.makedirs(folder) base = os.path.basename(self.image) name = folder + '/' + \ os.path.splitext(base)[0] + '_' + prefix + '.' + extension return name def save_Points(self, pref, poInt): """saves correlated points and reference points array to a txt file """ folderLocation = os.path.dirname(self.image) folderLocation = folderLocation + '/rawdata/%s' % pref if not os.path.exists(folderLocation): os.makedirs(folderLocation) num = os.path.splitext(os.path.basename(self.image))[0] np.savetxt('%s/%s_%s.txt' % (folderLocation, pref, num), poInt, delimiter=',') def correlation_Image(self): """Draws image correlation data points on reference image for each data point""" name = self.directory_Name('marker', 'png') display_Image(self.image, point=self.correlated_point, l_color=(0, 120, 255), p_color=(140, 130, 0), file_Name=name, text=name) self.save_Points('correlatedPoints', self.correlated_point) def displacement_Image(self, scale): """Draws displacement image on reference image for each data point. Parameters ---------- scale : int to amplify the displacement """ name = self.directory_Name('disp', 'png') display_Image(self.reference_image, point=self.reference_point, pointf=self.correlated_point, l_color=(125, 0, 125), p_color=(125, 125, 125), scale=scale, file_Name=name, text=name) self.save_Points('DisplacementPoints', self.reference_point) def surface_Image(self, scale): """Draws mesh deformations on reference image for each data point. Parameters ---------- scale : int to amplify the deformation """ name = self.directory_Name('surface', 'png') display_Image(self.reference_image, grid=self, scale=scale, surfColor=(255, 0, 250), file_Name=name, text=name) def raw_Data_CSV(self): """writes a csv file for displacement, strain and other parameters. This data can be used for post processing""" name = self.directory_Name('result', 'csv') f = open(name, 'w') f.write("index" + ',' + "index_x" + ',' + "index_y" + ',' + "x (px)" + ',' + "y (px)" + ',' + "x_Displacement" + ',' + "y_Displacement" + ',' + "x_Strain" + ',' + "y_Strain" + ',' + "xy_Strain" + '\n') index = 0 for i in range(self.size_x): for j in range(self.size_y): f.write( str(index) + ',' + str(i) + ',' + str(j) + ',' + str(self.Points_X[i, j]) + ',' + str(self.Points_Y[i, j]) + ',' + str(self.disp_x[i, j]) + ',' + str(self.disp_y[i, j]) + ',' + str(self.strain_longitudinal[i, j]) + ',' + str(self.strain_transverse[i, j]) + ',' + str(self.strain_xy[i, j]) + '\n') index = index + 1 f.close() def insitu_Plot(self, field, title, fig_No, img_file_Name): """Connects with Plot Class. Plots in-situ strain field using matplotlib interactive mapping""" img_folder = './Test_%s/*.jpg' % img_file_Name img_listt = sorted(glob.glob(img_folder), key=lambda t: os.stat(t).st_mtime) image_ref = cv2.imread(img_listt[0], 0) Plot(image_ref, self, field, title, fig_No) def bivariate_Interpolation(self, point, disp, *args, **kwargs): """Interpolates the displacement field. Bivariate B-spline interpolation algorithm from scipy is used here. for no interpolation option this can use raw method""" dx = np.array([d[0] for d in disp]) dy = np.array([d[1] for d in disp]) method = 'raw' if 'method' not in kwargs else kwargs['method'] if method == 'raw': self.disp_x = self.Points_X.copy() self.disp_y = self.Points_Y.copy() count = 0 for i in range(self.disp_x.shape[0]): for j in range(self.disp_x.shape[1]): self.disp_x[i, j] = dx[count] self.disp_y[i, j] = dy[count] count = count + 1 elif method == 'bivar_Spline': tck_x = scipy.interpolate.bisplrep(self.Points_X, self.Points_Y, dx, kx=5, ky=5) self.disp_x = scipy.interpolate.bisplev(self.Points_X[:, 0], self.Points_Y[0, :], tck_x) tck_y = scipy.interpolate.bisplrep(self.Points_X, self.Points_Y, dy, kx=5, ky=5) self.disp_y = scipy.interpolate.bisplev(self.Points_X[:, 0], self.Points_Y[0, :], tck_y) def strain_Field_Compute(self): """Computes Green-Langragian strain field from interpolated displacement data using numpy""" dx = self.Points_X[1][0] - self.Points_X[0][0] dy = self.Points_Y[0][1] - self.Points_Y[0][0] strain_longitudinal, strain_xy = np.gradient( self.disp_x, dx, dy, edge_order=2) strain_yx, strain_transverse = np.gradient( self.disp_y, dx, dy, edge_order=2) self.strain_longitudinal = strain_longitudinal + .5 * \ (np.power(strain_longitudinal, 2) + np.power(strain_xy, 2)) self.strain_transverse = strain_transverse + .5 * \ (np.power(strain_transverse, 2) + np.power(strain_yx, 2)) self.strain_xy = .5 * (strain_xy + strain_yx + strain_longitudinal * strain_xy + strain_yx * strain_transverse)
Methods
def bivariate_Interpolation(self, point, disp, *args, **kwargs)
-
Interpolates the displacement field. Bivariate B-spline interpolation algorithm from scipy is used here. for no interpolation option this can use raw method
Expand source code
def bivariate_Interpolation(self, point, disp, *args, **kwargs): """Interpolates the displacement field. Bivariate B-spline interpolation algorithm from scipy is used here. for no interpolation option this can use raw method""" dx = np.array([d[0] for d in disp]) dy = np.array([d[1] for d in disp]) method = 'raw' if 'method' not in kwargs else kwargs['method'] if method == 'raw': self.disp_x = self.Points_X.copy() self.disp_y = self.Points_Y.copy() count = 0 for i in range(self.disp_x.shape[0]): for j in range(self.disp_x.shape[1]): self.disp_x[i, j] = dx[count] self.disp_y[i, j] = dy[count] count = count + 1 elif method == 'bivar_Spline': tck_x = scipy.interpolate.bisplrep(self.Points_X, self.Points_Y, dx, kx=5, ky=5) self.disp_x = scipy.interpolate.bisplev(self.Points_X[:, 0], self.Points_Y[0, :], tck_x) tck_y = scipy.interpolate.bisplrep(self.Points_X, self.Points_Y, dy, kx=5, ky=5) self.disp_y = scipy.interpolate.bisplev(self.Points_X[:, 0], self.Points_Y[0, :], tck_y)
def correlation_Image(self)
-
Draws image correlation data points on reference image for each data point
Expand source code
def correlation_Image(self): """Draws image correlation data points on reference image for each data point""" name = self.directory_Name('marker', 'png') display_Image(self.image, point=self.correlated_point, l_color=(0, 120, 255), p_color=(140, 130, 0), file_Name=name, text=name) self.save_Points('correlatedPoints', self.correlated_point)
def directory_Name(self, prefix, extension)
-
Makes specific directories for storing specific results including surface image, grid displacement, raw data in CSV format
Expand source code
def directory_Name(self, prefix, extension): """Makes specific directories for storing specific results including surface image, grid displacement, raw data in CSV format""" folder = os.path.dirname(self.image) folder = folder + '/rawdata/' + prefix if not os.path.exists(folder): os.makedirs(folder) base = os.path.basename(self.image) name = folder + '/' + \ os.path.splitext(base)[0] + '_' + prefix + '.' + extension return name
def displacement_Image(self, scale)
-
Draws displacement image on reference image for each data point. Parameters
scale
:int
- to amplify the displacement
Expand source code
def displacement_Image(self, scale): """Draws displacement image on reference image for each data point. Parameters ---------- scale : int to amplify the displacement """ name = self.directory_Name('disp', 'png') display_Image(self.reference_image, point=self.reference_point, pointf=self.correlated_point, l_color=(125, 0, 125), p_color=(125, 125, 125), scale=scale, file_Name=name, text=name) self.save_Points('DisplacementPoints', self.reference_point)
def init_Raw_Data(self, winsize, reference_image, image, reference_point, correlated_point, disp)
-
Saves raw image data using opencv to current grid object
Expand source code
def init_Raw_Data(self, winsize, reference_image, image, reference_point, correlated_point, disp): """Saves raw image data using opencv to current grid object""" self.winsize = winsize self.reference_image = reference_image self.image = image self.reference_point = reference_point self.correlated_point = correlated_point self.disp = disp
def insitu_Plot(self, field, title, fig_No, img_file_Name)
-
Connects with Plot Class. Plots in-situ strain field using matplotlib interactive mapping
Expand source code
def insitu_Plot(self, field, title, fig_No, img_file_Name): """Connects with Plot Class. Plots in-situ strain field using matplotlib interactive mapping""" img_folder = './Test_%s/*.jpg' % img_file_Name img_listt = sorted(glob.glob(img_folder), key=lambda t: os.stat(t).st_mtime) image_ref = cv2.imread(img_listt[0], 0) Plot(image_ref, self, field, title, fig_No)
def raw_Data_CSV(self)
-
writes a csv file for displacement, strain and other parameters. This data can be used for post processing
Expand source code
def raw_Data_CSV(self): """writes a csv file for displacement, strain and other parameters. This data can be used for post processing""" name = self.directory_Name('result', 'csv') f = open(name, 'w') f.write("index" + ',' + "index_x" + ',' + "index_y" + ',' + "x (px)" + ',' + "y (px)" + ',' + "x_Displacement" + ',' + "y_Displacement" + ',' + "x_Strain" + ',' + "y_Strain" + ',' + "xy_Strain" + '\n') index = 0 for i in range(self.size_x): for j in range(self.size_y): f.write( str(index) + ',' + str(i) + ',' + str(j) + ',' + str(self.Points_X[i, j]) + ',' + str(self.Points_Y[i, j]) + ',' + str(self.disp_x[i, j]) + ',' + str(self.disp_y[i, j]) + ',' + str(self.strain_longitudinal[i, j]) + ',' + str(self.strain_transverse[i, j]) + ',' + str(self.strain_xy[i, j]) + '\n') index = index + 1 f.close()
def save_Points(self, pref, poInt)
-
saves correlated points and reference points array to a txt file
Expand source code
def save_Points(self, pref, poInt): """saves correlated points and reference points array to a txt file """ folderLocation = os.path.dirname(self.image) folderLocation = folderLocation + '/rawdata/%s' % pref if not os.path.exists(folderLocation): os.makedirs(folderLocation) num = os.path.splitext(os.path.basename(self.image))[0] np.savetxt('%s/%s_%s.txt' % (folderLocation, pref, num), poInt, delimiter=',')
def strain_Field_Compute(self)
-
Computes Green-Langragian strain field from interpolated displacement data using numpy
Expand source code
def strain_Field_Compute(self): """Computes Green-Langragian strain field from interpolated displacement data using numpy""" dx = self.Points_X[1][0] - self.Points_X[0][0] dy = self.Points_Y[0][1] - self.Points_Y[0][0] strain_longitudinal, strain_xy = np.gradient( self.disp_x, dx, dy, edge_order=2) strain_yx, strain_transverse = np.gradient( self.disp_y, dx, dy, edge_order=2) self.strain_longitudinal = strain_longitudinal + .5 * \ (np.power(strain_longitudinal, 2) + np.power(strain_xy, 2)) self.strain_transverse = strain_transverse + .5 * \ (np.power(strain_transverse, 2) + np.power(strain_yx, 2)) self.strain_xy = .5 * (strain_xy + strain_yx + strain_longitudinal * strain_xy + strain_yx * strain_transverse)
def surface_Image(self, scale)
-
Draws mesh deformations on reference image for each data point. Parameters
scale
:int
- to amplify the deformation
Expand source code
def surface_Image(self, scale): """Draws mesh deformations on reference image for each data point. Parameters ---------- scale : int to amplify the deformation """ name = self.directory_Name('surface', 'png') display_Image(self.reference_image, grid=self, scale=scale, surfColor=(255, 0, 250), file_Name=name, text=name)