python/lib/python3.6/site-packages/viewscad/renderer.py
如果你使用的openscad版本过旧(ubuntu16.04 是openscad2015)
通过python代码viewscad调用的时候,可能会出现错误,需要新版本的openscad
你可以修改renderer.py
def _try_detect_openscad_exec(self):
self.openscad_exec = None
platfm = platform.system()
if platfm == 'Linux':
self._try_executable('/usr/bin/openscad')
if self.openscad_exec is None:
#self._try_executable('/usr/local/bin/openscad')
self._try_executable('~/下载/OpenSCAD-2021.01-x86_64.AppImage')
elif platfm == 'Darwin':
self._try_executable('/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD')
elif platfm == 'Windows':
self._try_executable(os.path.join(
os.environ.get('Programfiles(x86)','C:'),
'OpenSCAD\\openscad.exe'))
https://stackoverflow.com/questions/60562858/best-libraries-for-openscad
0)
https://openscad.org/libraries.html1)
https://github.com/cznewt/openscad-model-library
https://openscad-model-library.readthedocs.io/en/latest/primitives.html#bolts-and-nuts2)
https://github.com/revarbat/BOSL
https://github.com/revarbat/BOSL/wiki3)
https://github.com/revarbat/BOSL2
https://github.com/revarbat/BOSL2/wiki4)
https://github.com/Irev-Dev/Round-Anything
https://kurthutten.com/blog/round-anything-a-pragmatic-approach-to-openscad-design/5)
https://github.com/nophead/NopSCADlib
https://github.com/nophead/NopSCADlib/blob/master/readme.md6)
https://github.com/boltsparts/BOLTS 推荐这个
https://www.bolts-library.org/en/index.html7)
https://github.com/JustinSDK/dotSCAD8)
https://github.com/openscad/MCAD
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/MCAD#Overview_of_MCAD_Library9)
https://www.thingiverse.com/thing:321599710)
https://github.com/openscad/list-comprehension-demos11)
https://github.com/davidson16807/relativity.scad
模型下载:
GitHub - rcolyer/threads-scad: OpenSCAD threading library
GitHub - GillesBouissac/agentscad: My utilities for OpenSCAD
GitHub - openscad/MCAD: OpenSCAD Parametric CAD Library (LGPL 2.1)
https://www.bolts-library.org/en/index.html
https://github.com/boltsparts/BOLTS
https://www.bolts-library.org/en/parts/index.html
Metric bolts with OpenSCAD by Claymore - Thingiverse
Nuts and Bolts v1.95 OpenSCAD library by biomushroom - Thingiverse
OpenSCAD教程-初学者的5个简单步骤
OpenSCAD中使用include或use引入外部库
OpenSCAD中使用include或use引入外部库_weixin_34221112的博客-CSDN博客
hex_head2.scad
$fn=100;
module hex_head(diameter,height) {
intersection() {
union() {
translate([0,0,0.801*height])
cylinder(d1=diameter,d2=0.8*diameter,h=0.2*height);
cylinder(d=diameter,h=0.801*height);
}
cylinder(d=diameter,h=height,$fn=6);
}
}
module washer(outside_diameter,inside_diameter,height) {
difference() {
cylinder(d=outside_diameter,h=height);
translate([0,0,-0.01])
cylinder(d=inside_diameter,h=height+0.02);
}
}
module steel_plate(side,thickness) {
translate([-side/2,-side/2,0])
cube([side,side,thickness]);
}
module screw(diameter,height) {
cylinder(d=diameter, h=height);
}
module hex_head2(tran_init,plate_side,plate_height,washer_out_diam,washer_in_diam,washer_height,bolt_height,bolt_diam,bolt_tran,bolt_rot,screw_diam,screw_height){
union(){
translate([0,0,tran_init])
steel_plate(plate_side,plate_height);
translate([0,0,tran_init+plate_height])
washer(washer_out_diam, washer_in_diam, washer_height);
translate([0,0,tran_init+plate_height+washer_height+bolt_tran])
rotate([0,0,bolt_rot])
hex_head(bolt_diam, bolt_height);
translate([0,0,tran_init+plate_height+washer_height])
screw(screw_diam, screw_height);
}
}
//平面距离坐标原点的高度
tran_init = 0;
//底平面参数
plate_side = 70;
plate_height = 2;
//垫片参数
washer_out_diam = 50;
washer_in_diam = 0;
washer_height = 3;
//螺母参数
bolt_height = 22; //大小-高度
bolt_diam = 35; //大小-宽度
bolt_tran = 20; //上下-高度
bolt_rot = 130; // degrees
//螺柱参数
screw_diam = 16;
screw_height = 44;
hex_head2(tran_init,plate_side,plate_height,washer_out_diam,washer_in_diam,washer_height,bolt_height,bolt_diam,bolt_tran,bolt_rot,screw_diam,screw_height);
demo_bolt_002.py
# python3.8
# -*- coding: utf-8 -*-
# ---
# @Software: PyCharm
# @File: demo_bolt_002.py
# @Author: ---
# @Institution: BeiJing, China
# @E-mail: lgdyangninghua@163.com
# @Site:
# @Time: 5月 12, 2021
# ---
import argparse
from solid import *
from solid.utils import *
import viewscad
import open3d as o3d
import os
import pathlib
scad_parameter={
#平面距离坐标原点的高度
'tran_init': 0,
#底平面参数
'plate_side': 70,
'plate_height': 2,
#垫片参数
'washer_out_diam': 50,
'washer_in_diam': 0,
'washer_height': 3,
#螺母参数
'bolt_height': 22, #大小-高度
'bolt_diam': 35, #大小-宽度
'bolt_tran': 15, #上下-高度
'bolt_rot': 15, #角度
#螺柱参数
'screw_diam': 16,
'screw_height': 44,
}
def mkdir_os(path):
if not os.path.exists(path):
os.makedirs(path)
def main(args):
scad_path = pathlib.Path(args.input_scad)
if not scad_path.is_file():
print("input_scad error")
return
mkdir_os(args.output_stl_folder)
mkdir_os(args.output_ply_folder)
scad_F = import_scad(args.input_scad)
index = 0
count = scad_parameter['screw_height'] * (180/10)
for nut_height in range(0, scad_parameter['screw_height'], 1):
for nut_angle in range(0, 180, 10):
index += 1
print(index, "/", count)
save_stl_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.stl".format(index, nut_height, nut_angle)
save_ply_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.ply".format(index, nut_height, nut_angle)
stl_path = os.path.join(args.output_stl_folder, save_stl_name)
ply_path = os.path.join(args.output_ply_folder, save_ply_name)
scad_model = scad_F.hex_head2(tran_init=scad_parameter['tran_init'],
plate_side=scad_parameter['plate_side'],
plate_height=scad_parameter['plate_height'],
washer_out_diam=scad_parameter['washer_out_diam'],
washer_in_diam=scad_parameter['washer_in_diam'],
washer_height=scad_parameter['washer_height'],
bolt_height=scad_parameter['bolt_height'],
bolt_diam=scad_parameter['bolt_diam'],
bolt_tran=nut_height,
bolt_rot=nut_angle,
screw_diam=scad_parameter['screw_diam'],
screw_height=scad_parameter['screw_height'])
v_rander = viewscad.Renderer()
v_rander.render(scad_model, outfile=stl_path)
#pymeshlab
import pymeshlab as ml
ms = ml.MeshSet()
ms.load_new_mesh(stl_path)
filename_mesh_out = os.path.join(args.output_stl_folder, "pymeshlab_"+save_stl_name)
ms.save_current_mesh(filename_mesh_out)
#这里之所以使用pymeshlab保存的stl,是因为open3d0.9.0有点问题,只能打开二进制的stl
#https://github.com/intel-isl/Open3D/issues/2479
#open3d
mesh_bolt = o3d.io.read_triangle_mesh(filename_mesh_out)
#mesh_bolt.paint_uniform_color([0, 0, 1])
pcd_mesh_bolt = mesh_bolt.sample_points_poisson_disk(number_of_points=15000)
#pcd_mesh_bolt.paint_uniform_color([1, 1, 0])
#print('pcd_mesh_bolt:', pcd_mesh_bolt)
pcd_mesh_bolt = pcd_mesh_bolt.voxel_down_sample(voxel_size=1)
#print('pcd_mesh_bolt', pcd_mesh_bolt)
o3d.io.write_point_cloud(ply_path, pcd_mesh_bolt)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="creat bolt")
parser.add_argument('-if',
"--input_scad",
default='./hex_head2.scad',
help="set input scad file")
parser.add_argument('-os',
"--output_stl_folder",
default='./result_stl/',
help="set output stl folder")
parser.add_argument('-op',
"--output_ply_folder",
default='./result_ply/',
help="set output ply folder")
args = parser.parse_args()
if args.input_scad is None:
parser.print_help()
exit()
main(args)
'''
https://openscad.org/
https://github.com/nickc92/ViewSCAD
https://github.com/SolidCode/SolidPython
https://github.com/cnr-isti-vclab/PyMeshLab
python -mpip install open3d==0.9.0
sudo apt-get install openscad (2015.03-1+dfsg-3)
solid 0.2.0
solidpython 0.4.2
python -mpip install solid==0.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
python -mpip install solidpython==0.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
jupyter可视化
python -mpip install viewscad==0.2.0
安装插件:
jupyter labextension install @jupyter-widgets/jupyterlab-manager
https://pypi.org/project/jupyter-openscad-kernel/
https://pymeshlab.readthedocs.io/en/latest/
python -mpip install pymeshlab==0.2
'''
demo_bolt_003.py
带标签的数据生成:
# python3.8
# -*- coding: utf-8 -*-
# ---
# @Software: PyCharm
# @File: demo_bolt_002.py
# @Author: ---
# @Institution: BeiJing, China
# @E-mail: lgdyangninghua@163.com
# @Site:
# @Time: 5月 12, 2021
# ---
import argparse
from solid import *
from solid.utils import *
import viewscad
import open3d as o3d
import os
import pathlib
import copy
import numpy as np
HEADER = '''\
# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z label
SIZE 4 4 4 4
TYPE F F F I
COUNT 1 1 1 1
WIDTH {}
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS {}
DATA ascii
'''
def write_pcd_fun1(points, save_pcd_path):
n = len(points)
lines = []
for i in range(n):
x, y, z, label = points[i]
lines.append('{:.6f} {:.6f} {:.6f} {}'.format(x, y, z, label))
with open(save_pcd_path, 'w') as f:
f.write(HEADER.format(n, n))
f.write('\n'.join(lines))
def write_pcd_fun2(points, save_pcd_path):
with open(save_pcd_path, 'w') as f:
f.write(HEADER.format(len(points), len(points)) + '\n')
np.savetxt(f, points, delimiter=' ', fmt='%f %f %f %d')
#https://blog.csdn.net/weixin_48994268/article/details/115682269
#open3d连续读取pcd文件及实现点云视角转换
def save_view_point(pcd, filename):
vis = o3d.visualization.Visualizer()
vis.create_window(window_name='pcd', width=800, height=600)
vis.add_geometry(pcd)
vis.run() # user changes the view and press "q" to terminate
param = vis.get_view_control().convert_to_pinhole_camera_parameters()
o3d.io.write_pinhole_camera_parameters(filename, param)
vis.destroy_window()
def load_view_point(pcd, filename):
vis = o3d.visualization.Visualizer()
vis.create_window(window_name='pcd', width=800, height=600)
ctr = vis.get_view_control()
param = o3d.io.read_pinhole_camera_parameters(filename)
vis.add_geometry(pcd)
ctr.convert_from_pinhole_camera_parameters(param)
vis.run()
vis.destroy_window()
scad_parameter={
#平面距离坐标原点的高度
'tran_init': 0,
#底平面参数
'plate_side': 70,
'plate_height': 1,
#垫片参数
'washer_out_diam': 50,
'washer_in_diam': 0,
'washer_height': 2,
#螺母参数
'bolt_height': 22, #大小-高度
'bolt_diam': 35, #大小-宽度
'bolt_tran': 15, #上下-高度
'bolt_rot': 15, #角度
#螺柱参数
'screw_diam': 16,
'screw_height': 44,
}
label_dict = {
"other": 0, #washer
"stud": 1,
"plane": 2,
"nut": 3,
}
def mkdir_os(path):
if not os.path.exists(path):
os.makedirs(path)
def main(args):
scad_path = pathlib.Path(args.input_scad)
if not scad_path.is_file():
print("input_scad error")
return
mkdir_os(args.output_stl_folder)
mkdir_os(args.output_ply_folder)
scad_F = import_scad(args.input_scad)
index = 0
count = scad_parameter['screw_height'] * (180/10)
for nut_height in range(0, scad_parameter['screw_height'], 1):
for nut_angle in range(0, 180, 10):
index += 1
print(index, "/", count)
save_stl_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.stl".format(index, nut_height, nut_angle)
save_ply_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.ply".format(index, nut_height, nut_angle)
stl_path = os.path.join(args.output_stl_folder, save_stl_name)
ply_path = os.path.join(args.output_ply_folder, save_ply_name)
scad_model = scad_F.hex_head2(tran_init=scad_parameter['tran_init'],
plate_side=scad_parameter['plate_side'],
plate_height=scad_parameter['plate_height'],
washer_out_diam=scad_parameter['washer_out_diam'],
washer_in_diam=scad_parameter['washer_in_diam'],
washer_height=scad_parameter['washer_height'],
bolt_height=scad_parameter['bolt_height'],
bolt_diam=scad_parameter['bolt_diam'],
bolt_tran=nut_height,
bolt_rot=nut_angle,
screw_diam=scad_parameter['screw_diam'],
screw_height=scad_parameter['screw_height'])
#分开生成形成label
#垫圈+other
washer = copy.deepcopy(scad_model)
washer.params["plate_height"] = 0
washer.params["bolt_height"] = 0
washer.params["screw_height"] = 0
#螺柱
stud = copy.deepcopy(scad_model)
stud.params["plate_height"] = 0
stud.params["washer_height"] = 0
stud.params["bolt_height"] = 0
#底平面
plane = copy.deepcopy(scad_model)
plane.params["washer_height"] = 0
plane.params["bolt_height"] = 0
plane.params["screw_height"] = 0
#螺母
nut = copy.deepcopy(scad_model)
nut.params["plate_height"] = 0
nut.params["washer_height"] = 0
nut.params["screw_height"] = 0
model_list = [washer, stud, plane, nut]
mesh_list = []
color_list = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[1, 1, 0]
]
for ind, mod in enumerate(model_list):
v_rander = viewscad.Renderer()
v_rander.render(mod, outfile=stl_path) #这里已经保存了stl,但是是ascii format
#pymeshlab
import pymeshlab as ml
ms = ml.MeshSet()
ms.load_new_mesh(stl_path)
filename_mesh_out = os.path.join(args.output_stl_folder, "pymeshlab_"+save_stl_name)
ms.save_current_mesh(filename_mesh_out)
#这里之所以使用pymeshlab保存的stl,是因为open3d0.9.0有点问题,只能打开二进制的stl
#https://github.com/intel-isl/Open3D/issues/2479
#open3d
mesh_bolt = o3d.io.read_triangle_mesh(filename_mesh_out)
mesh_bolt.paint_uniform_color(color_list[ind])
mesh_list.append(mesh_bolt)
model = mesh_list[0] + mesh_list[1] + mesh_list[2] + mesh_list[3]
model_point = model.sample_points_poisson_disk(number_of_points=15000)
print('model_point:', model_point)
model_point = model_point.voxel_down_sample(voxel_size=1)
print('model_point', model_point)
#螺柱在螺母中的也会显示
# o3d.visualization.draw_geometries([#model,
# model_point,
# #model_point.get_axis_aligned_bounding_box(),
# #model.get_axis_aligned_bounding_box(),
# #o3d.geometry.TriangleMesh.create_coordinate_frame(size=20)
# ])
#虽然这种方法可以保存视角,但是无法与hidden_point_removal联合使用
#save_view_point(model_point, "viewpoint.json")
#load_view_point(model_point, "viewpoint.json")
#如果想要螺柱在螺母中不显示,需要用到视角遮挡消除,但是视角计算的时候使用的是计算出来的视角
'''
| hidden_point_removal(...)
| hidden_point_removal(self, camera_location, radius)
|
| Removes hidden points from a point cloud and returns a mesh of the remaining points. Based on Katz et al. 'Direct Visibility of Point Sets', 2007.
|
| Args:
| camera_location (numpy.ndarray[float64[3, 1]]): All points not visible from that location will be reomved
| radius (float): The radius of the sperical projection
|
| Returns:
| Tuple[open3d.geometry.TriangleMesh, List[int]]
'''
diameter = np.linalg.norm(np.asarray(model_point.get_max_bound()) - np.asarray(model_point.get_min_bound()))
#camera = [0, 0, diameter]
camera = [ diameter/2, diameter/2, diameter]
#camera = [0, 0, 44]
radius = diameter*100
print("Get all points that are visible from given view point")
_, pt_map = model_point.hidden_point_removal(camera, radius)
#https://github.com/intel-isl/Open3D/issues/1860
#open3d-0.9.0
#select_point = model_point.select_by_index(pt_map)
select_point = model_point.select_down_sample(pt_map)
o3d.visualization.draw_geometries([select_point,
o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
o3d.geometry.TriangleMesh.create_coordinate_frame(size=20, origin=camera),
select_point.get_axis_aligned_bounding_box(),
])
#通过颜色确定label,保存为pcd
label = model_point.colors
np_label = np.asarray(label).astype(np.int)
points = model_point.points
np_points = np.asarray(points)
ind_washer = np.intersect1d(np.where(np_label[:,0] == 1), np.where(np_label[:,1] == 0), np.where(np_label[:,2] == 0))
ind_stud = np.intersect1d(np.where(np_label[:,0] == 0), np.where(np_label[:,1] == 1), np.where(np_label[:,2] == 0))
ind_plane = np.intersect1d(np.where(np_label[:,0] == 0), np.where(np_label[:,1] == 0), np.where(np_label[:,2] == 1))
ind_nut = np.intersect1d(np.where(np_label[:,0] == 1), np.where(np_label[:,1] == 1), np.where(np_label[:,2] == 0))
np_washer = np_points[ind_washer][:, :3]
np_stud = np_points[ind_stud][:, :3]
np_plane = np_points[ind_plane][:, :3]
np_nut = np_points[ind_nut][:, :3]
t_lab = np.ones((np_washer.shape[0], 1), dtype=np.int8) * 0
p_washer = np.hstack((np_washer, t_lab))
t_lab = np.ones((np_stud.shape[0], 1), dtype=np.int8) * 1
p_stud = np.hstack((np_stud, t_lab))
t_lab = np.ones((np_plane.shape[0], 1), dtype=np.int8) * 2
p_plane = np.hstack((np_plane, t_lab))
t_lab = np.ones((np_nut.shape[0], 1), dtype=np.int8) * 3
p_nut = np.hstack((np_nut, t_lab))
write_pcd_fun1(np.vstack((p_washer, p_stud, p_plane, p_nut)), ply_path.replace('.ply', '.pcd'))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="creat bolt")
parser.add_argument('-if',
"--input_scad",
default='./hex_head2.scad',
help="set input scad file")
parser.add_argument('-os',
"--output_stl_folder",
default='./result_stl/',
help="set output stl folder")
parser.add_argument('-op',
"--output_ply_folder",
default='./result_ply/',
help="set output ply folder")
args = parser.parse_args()
if args.input_scad is None:
parser.print_help()
exit()
main(args)
'''
https://openscad.org/
https://github.com/nickc92/ViewSCAD
https://github.com/SolidCode/SolidPython
https://github.com/cnr-isti-vclab/PyMeshLab
python -mpip install open3d==0.9.0
sudo apt-get install openscad (2015.03-1+dfsg-3)
solid 0.2.0
solidpython 0.4.2
python -mpip install solid==0.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
python -mpip install solidpython==0.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
jupyter可视化
python -mpip install viewscad==0.2.0
安装插件:
jupyter labextension install @jupyter-widgets/jupyterlab-manager
https://pypi.org/project/jupyter-openscad-kernel/
https://pymeshlab.readthedocs.io/en/latest/
python -mpip install pymeshlab==0.2
'''
右手坐标系统,X = Red, Y = Green, Z = Blue
demo_bolt_004.py
# python3.8
# -*- coding: utf-8 -*-
# ---
# @Software: PyCharm
# @File: demo_bolt_002.py
# @Author: ---
# @Institution: BeiJing, China
# @E-mail: lgdyangninghua@163.com
# @Site:
# @Time: 5月 12, 2021
# ---
import argparse
from solid import *
from solid.utils import *
import viewscad
import open3d as o3d
import os
import pathlib
import copy
import numpy as np
HEADER = '''\
# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z label
SIZE 4 4 4 4
TYPE F F F I
COUNT 1 1 1 1
WIDTH {}
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS {}
DATA ascii
'''
def write_pcd_fun1(points, save_pcd_path):
n = len(points)
lines = []
for i in range(n):
x, y, z, label = points[i]
lines.append('{:.6f} {:.6f} {:.6f} {}'.format(x, y, z, label))
with open(save_pcd_path, 'w') as f:
f.write(HEADER.format(n, n))
f.write('\n'.join(lines))
def write_pcd_fun2(points, save_pcd_path):
with open(save_pcd_path, 'w') as f:
f.write(HEADER.format(len(points), len(points)) + '\n')
np.savetxt(f, points, delimiter=' ', fmt='%f %f %f %d')
#https://blog.csdn.net/weixin_48994268/article/details/115682269
#open3d连续读取pcd文件及实现点云视角转换
def save_view_point(pcd, filename):
vis = o3d.visualization.Visualizer()
vis.create_window(window_name='pcd', width=800, height=600)
vis.add_geometry(pcd)
vis.run() # user changes the view and press "q" to terminate
param = vis.get_view_control().convert_to_pinhole_camera_parameters()
o3d.io.write_pinhole_camera_parameters(filename, param)
vis.destroy_window()
def load_view_point(pcd, filename):
vis = o3d.visualization.Visualizer()
vis.create_window(window_name='pcd', width=800, height=600)
ctr = vis.get_view_control()
param = o3d.io.read_pinhole_camera_parameters(filename)
vis.add_geometry(pcd)
ctr.convert_from_pinhole_camera_parameters(param)
vis.run()
vis.destroy_window()
scad_parameter={
#平面距离坐标原点的高度
'tran_init': 0,
#底平面参数
'plate_side': 70,
'plate_height': 1,
#垫片参数
'washer_out_diam': 50,
'washer_in_diam': 0,
'washer_height': 2,
#螺母参数
'bolt_height': 22, #大小-高度
'bolt_diam': 35, #大小-宽度
'bolt_tran': 15, #上下-高度
'bolt_rot': 15, #角度
#螺柱参数
'screw_diam': 16,
'screw_height': 44,
}
label_dict = {
"other": 0, #washer
"stud": 1,
"plane": 2,
"nut": 3,
}
def mkdir_os(path):
if not os.path.exists(path):
os.makedirs(path)
def main(args):
scad_path = pathlib.Path(args.input_scad)
if not scad_path.is_file():
print("input_scad error")
return
mkdir_os(args.output_stl_folder)
mkdir_os(args.output_ply_folder)
scad_F = import_scad(args.input_scad)
index = 0
count = scad_parameter['screw_height'] * (180/10)
for nut_height in range(0, scad_parameter['screw_height'], 2):
for nut_angle in range(0, 180, 75):
index += 1
print(index, "/", count)
save_stl_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}.stl".format(index, nut_height, nut_angle)
stl_path = os.path.join(args.output_stl_folder, save_stl_name)
scad_model = scad_F.hex_head2(tran_init=scad_parameter['tran_init'],
plate_side=scad_parameter['plate_side'],
plate_height=scad_parameter['plate_height'],
washer_out_diam=scad_parameter['washer_out_diam'],
washer_in_diam=scad_parameter['washer_in_diam'],
washer_height=scad_parameter['washer_height'],
bolt_height=scad_parameter['bolt_height'],
bolt_diam=scad_parameter['bolt_diam'],
bolt_tran=nut_height,
bolt_rot=nut_angle,
screw_diam=scad_parameter['screw_diam'],
screw_height=scad_parameter['screw_height'])
#分开生成形成label
#垫圈+other
washer = copy.deepcopy(scad_model)
washer.params["plate_height"] = 0
washer.params["bolt_height"] = 0
washer.params["screw_height"] = 0
#螺柱
stud = copy.deepcopy(scad_model)
stud.params["plate_height"] = 0
stud.params["washer_height"] = 0
stud.params["bolt_height"] = 0
#底平面
plane = copy.deepcopy(scad_model)
plane.params["washer_height"] = 0
plane.params["bolt_height"] = 0
plane.params["screw_height"] = 0
#螺母
nut = copy.deepcopy(scad_model)
nut.params["plate_height"] = 0
nut.params["washer_height"] = 0
nut.params["screw_height"] = 0
model_list = [washer, stud, plane, nut]
mesh_list = []
color_list = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[1, 1, 0]
]
for ind, mod in enumerate(model_list):
v_rander = viewscad.Renderer()
v_rander.render(mod, outfile=stl_path) #这里已经保存了stl,但是是ascii format
#pymeshlab
import pymeshlab as ml
ms = ml.MeshSet()
ms.load_new_mesh(stl_path)
filename_mesh_out = os.path.join(args.output_stl_folder, "pymeshlab_"+save_stl_name)
ms.save_current_mesh(filename_mesh_out)
#这里之所以使用pymeshlab保存的stl,是因为open3d0.9.0有点问题,只能打开二进制的stl
#https://github.com/intel-isl/Open3D/issues/2479
#open3d
mesh_bolt = o3d.io.read_triangle_mesh(filename_mesh_out)
mesh_bolt.paint_uniform_color(color_list[ind])
mesh_list.append(mesh_bolt)
model = mesh_list[0] + mesh_list[1] + mesh_list[2] + mesh_list[3]
model_point = model.sample_points_poisson_disk(number_of_points=15000)
print('model_point:', model_point)
model_point = model_point.voxel_down_sample(voxel_size=1)
print('model_point', model_point)
radius = 11000
camera3 = [60, 100]
for cam_z, cameraZ in enumerate(camera3):
save_ply_name = "bolt1_index{:0>5d}_nut_height-{}_nut_angle-{}_camZ-{}.ply".format(index, nut_height, nut_angle, cameraZ)
ply_path = os.path.join(args.output_ply_folder, save_ply_name)
# o3d.visualization.draw_geometries([model_point,
# o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
# model_point.get_axis_aligned_bounding_box(),
# ])
camera = [70, 70, cameraZ]
_, pt_map = model_point.hidden_point_removal(camera, radius)
select_point = model_point.select_down_sample(pt_map)
# o3d.visualization.draw_geometries([select_point,
# o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
# select_point.get_axis_aligned_bounding_box(),
# ])
#通过颜色确定label,保存为pcd
label = select_point.colors
np_label = np.asarray(label)
#不要使用取整,会发生[1 1 0]变成[0 0 0]的情况,具体我还不知道为什么
#np_label = np_label.astype(np.int)
#np_label = np.floor(np_label)
points = select_point.points
np_points = np.asarray(points)
ind_washer = np.intersect1d(np.where(np_label[:,0] >= 0.5), np.where(np_label[:,1] == 0), np.where(np_label[:,2] == 0))
ind_stud = np.intersect1d(np.where(np_label[:,0] == 0), np.where(np_label[:,1] >= 0.5), np.where(np_label[:,2] == 0))
ind_plane = np.intersect1d(np.where(np_label[:,0] == 0), np.where(np_label[:,1] == 0), np.where(np_label[:,2] >= 0.5))
ind_nut = np.intersect1d(np.where(np_label[:,0] >= 0.5), np.where(np_label[:,1] >= 0.5), np.where(np_label[:,2] == 0))
np_washer = np_points[ind_washer][:, :3]
np_stud = np_points[ind_stud][:, :3]
np_plane = np_points[ind_plane][:, :3]
np_nut = np_points[ind_nut][:, :3]
# pcd1 = o3d.geometry.PointCloud()
# pcd1.points = o3d.utility.Vector3dVector(np_washer)
# pcd1.paint_uniform_color(color_list[0])
# pcd2= o3d.geometry.PointCloud()
# pcd2.points = o3d.utility.Vector3dVector(np_stud)
# pcd2.paint_uniform_color(color_list[1])
# pcd3 = o3d.geometry.PointCloud()
# pcd3.points = o3d.utility.Vector3dVector(np_plane)
# pcd3.paint_uniform_color(color_list[2])
# pcd4 = o3d.geometry.PointCloud()
# pcd4.points = o3d.utility.Vector3dVector(np_nut)
# pcd4.paint_uniform_color(color_list[3])
# o3d.visualization.draw_geometries([pcd1,pcd2,pcd3,pcd4,
# o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
# select_point.get_axis_aligned_bounding_box(),
# ])
t_lab = np.ones((np_washer.shape[0], 1), dtype=np.int8) * 0
p_washer = np.hstack((np_washer, t_lab))
t_lab = np.ones((np_stud.shape[0], 1), dtype=np.int8) * 1
p_stud = np.hstack((np_stud, t_lab))
t_lab = np.ones((np_plane.shape[0], 1), dtype=np.int8) * 2
p_plane = np.hstack((np_plane, t_lab))
t_lab = np.ones((np_nut.shape[0], 1), dtype=np.int8) * 3
p_nut = np.hstack((np_nut, t_lab))
write_pcd_fun1(np.vstack((p_washer, p_stud, p_plane, p_nut)), ply_path.replace('.ply', '.pcd'))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="creat bolt")
parser.add_argument('-if',
"--input_scad",
default='./hex_head2.scad',
help="set input scad file")
parser.add_argument('-os',
"--output_stl_folder",
default='./result_stl/',
help="set output stl folder")
parser.add_argument('-op',
"--output_ply_folder",
default='./result_ply/',
help="set output ply folder")
args = parser.parse_args()
if args.input_scad is None:
parser.print_help()
exit()
main(args)
'''
https://openscad.org/
https://github.com/nickc92/ViewSCAD
https://github.com/SolidCode/SolidPython
https://github.com/cnr-isti-vclab/PyMeshLab
python -mpip install open3d==0.9.0
sudo apt-get install openscad (2015.03-1+dfsg-3)
solid 0.2.0
solidpython 0.4.2
python -mpip install solid==0.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
python -mpip install solidpython==0.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
jupyter可视化
python -mpip install viewscad==0.2.0
安装插件:
jupyter labextension install @jupyter-widgets/jupyterlab-manager
https://pypi.org/project/jupyter-openscad-kernel/
https://pymeshlab.readthedocs.io/en/latest/
python -mpip install pymeshlab==0.2
'''
生成shapenet格式
# python3.8
# -*- coding: utf-8 -*-
# ---
# @Software: PyCharm
# @File: creat_train_txt.py.py
# @Author: ---
# @Institution: BeiJing, China
# @E-mail: lgdyangninghua@163.com
# @Site:
# @Time: 5月 24, 2021
# ---
import os
import cv2
import open3d as o3d
import numpy as np
import json
def mkdir_os(path):
if not os.path.exists(path):
os.makedirs(path)
def get_plane(cloud, distance_threshold=0.01, ransac_n=10, num_iterations=200, rt_plane=False):
coefficients, inliers = cloud.segment_plane(distance_threshold=distance_threshold,
ransac_n=ransac_n,
num_iterations=num_iterations)
if rt_plane:
mmin = np.min(pcd2npy(cloud), axis=0).reshape(-1, 1)
mmax = np.max(pcd2npy(cloud), axis=0).reshape(-1, 1)
bbox = np.concatenate([mmin, mmax], axis=1)
x_range, y_range = bbox[0, :], bbox[1, :]
cloud_plane = generate_plane_points(coefficients, x_range, y_range)
return coefficients, cloud_plane
return coefficients
def points_plane_dists(points, coef):
ones = np.ones(shape=(len(points), 1)).astype(np.float32)
points = np.concatenate([points, ones], axis=1)
coef = np.array(coef).reshape(4, 1)
dists = np.squeeze(np.matmul(points, coef))
return dists
category = "luomu_openscad"
save_path = "shapenet"
mkdir_os(save_path)
mkdir_os(os.path.join(save_path, "train_test_split"))
mkdir_os(os.path.join(save_path, category))
data_path = "./result_ply"
lines = os.listdir(data_path)
'''
luomu模型:
label_dict = {
"other": 0, 背景(垫圈噪声等属于背景类)
"stud": 1, 螺柱(螺母上部分的螺栓点云才是我们的感兴趣点云)
"plane": 2, 平面
"nut": 3, 螺母
}
luoshuan模型:
label_dict = {
"other": 0, 背景(垫圈噪声等属于背景类)
"bolt": 1, 螺栓
"plane": 2, 平面
}
'''
train_list, test_list, val_list = [], [], []
for ind, line in enumerate(lines):
print(ind, "/", len(lines))
pcd = os.path.join(data_path, line)
with open(pcd, "r") as f:
data = f.readlines()
data_point3d = data[11:]
#该数据存在问题,我想要的是螺母之上的螺栓为label==1,螺母之下的label==0(背景类),若螺母拧过螺柱,那么此时没有点云label==1
other_list = []
stud_list = []
plane_list = []
nut_list = []
all_list = []
for m_line in data_point3d:
temp = m_line.split(" ")
label = int(float(temp[3].strip()))
if label == 0:
other_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
all_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
elif label == 1:
stud_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
elif label == 2:
plane_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
all_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
elif label == 3:
nut_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
all_list.append([float(temp[0]), float(temp[1]), float(temp[2]), label])
else:
exit()
color_list = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[1, 1, 0]
]
np_data = np.array(all_list)
other_o3d = o3d.geometry.PointCloud()
other_o3d.points = o3d.utility.Vector3dVector(np.array(other_list)[:, 0:3])
other_o3d.paint_uniform_color(color_list[0])
stud_o3d= o3d.geometry.PointCloud()
stud_o3d.points = o3d.utility.Vector3dVector(np.array(stud_list)[:, 0:3])
stud_o3d.paint_uniform_color(color_list[1])
plane_o3d = o3d.geometry.PointCloud()
plane_o3d.points = o3d.utility.Vector3dVector(np.array(plane_list)[:, 0:3])
plane_o3d.paint_uniform_color(color_list[2])
nut_o3d = o3d.geometry.PointCloud()
nut_o3d.points = o3d.utility.Vector3dVector(np.array(nut_list)[:, 0:3])
nut_o3d.paint_uniform_color(color_list[3])
# o3d.visualization.draw_geometries([other_o3d,stud_o3d,plane_o3d,nut_o3d,
# o3d.geometry.TriangleMesh.create_coordinate_frame(size=50),
# #select_point.get_axis_aligned_bounding_box(),
# ])
coefficients = get_plane(plane_o3d)
d_list = []
a, b, c = coefficients[0], coefficients[1], coefficients[2]
for index, pt in enumerate(np.array(nut_list)[:, 0:3]):
x, y, z = pt
if x != 0 and y != 0 and z != 0:
d = (0 - (a * x + b * y + c * z))
d_list.append(d)
d_list = np.sort(np.array(d_list), axis=0)
d_parameter = np.mean(d_list[5:10], axis=0, dtype=np.float32)
coefficients[3] = d_parameter
pp_dists = points_plane_dists(np.array(stud_list)[:, 0:3], coefficients)
ind_below = np.where(pp_dists<0)
ind_above = np.where(pp_dists>=0)
points_below = np.array(stud_list)[ind_below]
points_below[:, 3] = 0
points_above = np.array(stud_list)[ind_above]
if points_below.size != 0:
data_point3d = np.vstack((np_data, points_below))
if points_above.size != 0:
data_point3d = np.vstack((np_data, points_above))
with open(os.path.join(save_path, category, line.replace('.pcd', '.txt')), "w") as f:
for m_val in data_point3d:
f.write("{} {} {} 0.0 0.0 0.0 {}\n".format(m_val[0], m_val[1], m_val[2], m_val[3]))
train_list.append(os.path.join(save_path, category, line.split(".pcd")[0]))
with open(os.path.join(save_path, "synsetoffset2category.txt"), "w") as f:
f.write("luomu\tluomu_openscad\n")
with open(os.path.join(save_path, "train_test_split", "shuffled_test_file_list.json"), "w") as f_obj:
json.dump(train_list, f_obj, indent=1)
with open(os.path.join(save_path, "train_test_split", "shuffled_train_file_list.json"), "w") as f_obj:
json.dump(train_list, f_obj, indent=1)
with open(os.path.join(save_path, "train_test_split", "shuffled_val_file_list.json"), "w") as f_obj:
json.dump(train_list, f_obj, indent=1)