【问题标题】:Turn any 2D image into 3D printable sculpture with code使用代码将任何 2D 图像转换为 3D 可打印雕塑
【发布时间】:2015-09-28 02:21:13
【问题描述】:

我正在尝试仅使用代码将 2D 图像转换为 3D 可打印雕塑。首先我想知道它是否可以只用一个脚本来完成?我已经了解 Python 和 C,如果我可以使用其中之一来做我想做的事,当然会很棒。

这里有两个链接供您了解我所说的“将任何 2D 图像变成 3D 可打印雕塑”的意思(但这些都是使用软件):

https://www.youtube.com/watch?v=ngZwibfaysc

https://www.youtube.com/watch?v=-fe2zxcKSic

更具体地说,我想插入一个图像,然后等待获得结果,这将是一个 3D 雕塑。

【问题讨论】:

  • 你必须近似一个高度图;你可以用 gimp-cli 做到这一点,我认为不需要编写 python 脚本
  • 1.更具体地说,您想要真实的模型或只是照明编码的表面(以及以哪种形式......)。 2. 您将使用什么样的图像作为输入(通用在 CV/DIP 中不可行/不实用/可靠)。顺便说一句,在看过视频后,C/C++ 应该不会太难......

标签: python c image image-processing 3d


【解决方案1】:

有点好奇,所以我编码了一个照明表面编码的小例子

  • 对于输入图像的每个像素height = (color_intensity)*scale

这是我测试过的输入图像(Google 搜索中第一幅漂亮的油画)

这是结果(点云 3D 预览)

左侧是动画 gif,因此如果动画已经停止,请重新加载/刷新页面以查看动画,或者下载 gif 并以更简洁的方式打开,然后打开用于 gif 预览的browser...右侧是彩色点云预览(静态图片)

这是计算这个的 C++ 代码:

OpenGLtexture zed,nx,ny,nz; // height map,normal maps (just 2D images)
picture pic;                // source image

int x,y,a;
// resize textures to source image size
zed.resize(pic.xs,pic.ys); 
 nx.resize(pic.xs,pic.ys); float *pnx=(float*) nx.txr;
 ny.resize(pic.xs,pic.ys); float *pny=(float*) ny.txr;
 nz.resize(pic.xs,pic.ys); float *pnz=(float*) nz.txr;
// prepare tmp image for height map extraction
picture pic0;
pic0=pic;       // copy
pic0.rgb2i();   // grayscale

// this computes the point cloud (this is the only important stuff from this code)
// as you can see there are just 3 lines of code important from all of this
for (a=0,y=0;y<pic.ys;y++)
 for (x=0;x<pic.xs;x++,a++)
  zed.txr[a]=pic0.p[y][x].dd>>3; // height = intensity/(2^3)

// compute normals (for OpenGL rendering only)
double n[3],p0[3],px[3],py[3];
int zedx,zedy,picx,picy;
for (a=zed.xs,zedy=-(pic.ys>>1),picy=1;picy<pic.ys;picy++,zedy++)
 for (a++,    zedx=-(pic.xs>>1),picx=1;picx<pic.xs;picx++,zedx++,a++)
    {
    vector_ld(p0,zedx-1,zedy  ,-zed.txr[a       -1]); // 3 neighboring points
    vector_ld(py,zedx  ,zedy-1,-zed.txr[a+zed.xs  ]);
    vector_ld(px,zedx  ,zedy  ,-zed.txr[a         ]);
    vector_sub(px,p0,px); // 2 vectors (latices of quad/triangle)
    vector_sub(py,p0,py);
    vector_mul(n,px,py); // cross product
    vector_one(n,n); // unit vector normalization
    pnx[a]=n[0]; // store vector components to textures
    pny[a]=n[1];
    pnz[a]=n[2];
    }

这里是OpenGL预览代码(C++):

scr.cls(); // clear buffers

scr.set_perspective(); // set camera matrix
glMatrixMode(GL_MODELVIEW); // set object matrix
rep.use_rep();
glLoadMatrixd(rep.rep);

// directional (normal shading)
float lightAmbient  [4]={0.20,0.20,0.20,1.00};      
float lightDiffuse  [4]={1.00,1.00,1.00,1.00};      
float lightDirection[4]={0.00,0.00,+1.0,0.00};      
glLightfv(GL_LIGHT1,GL_AMBIENT ,lightAmbient );
glLightfv(GL_LIGHT1,GL_DIFFUSE ,lightDiffuse );
glLightfv(GL_LIGHT1,GL_POSITION,lightDirection);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);

glDisable(GL_TEXTURE_2D);
glEnable(GL_COLOR_MATERIAL);

// render point cloud
int zedx,zedy,picx,picy,a;
glColor3f(0.7,0.7,0.7);
float *pnx=(float*)nx.txr;
float *pny=(float*)ny.txr;
float *pnz=(float*)nz.txr;
glBegin(GL_POINTS);
for (a=zed.xs,zedy=-(pic.ys>>1),picy=1;picy<pic.ys;picy++,zedy++)
 for (a++,    zedx=-(pic.xs>>1),picx=1;picx<pic.xs;picx++,zedx++,a++)
    {
    //glColor4ubv((BYTE*)&pic.p[picy][picx].dd); // this is coloring with original image colors but it hides the 3D effect
    glNormal3f(pnx[a],pny[a],pnz[a]); // normal for lighting
    glVertex3i(zedx  ,zedy  ,-zed.txr[a]); // this is the point cloud surface point coordinate
    }
glEnd();

scr.exe(); // finalize OpenGL calls and swap buffers ...
scr.rfs();

矩阵设置如下:

// gluProjection parameters
double f=100;                   //[pixels] focus
scr.views[0].znear=       f;    //[pixels]
scr.views[0].zfar =1000.0+f;    //[pixels]
scr.views[0].zang =  60.0;      //[deg] view projection angle
scr.init(this); // this compute the Projection matrix and init OpenGL
// place the painting surface in the middle of frustrum
rep.reset();
rep.gpos_set(vector_ld(0.0,0.0,-0.5*(scr.views[0].zfar+scr.views[0].znear)));
rep.lrotx(180.0*deg); // rotate it to match original image

[注释]

我正在使用自己的图片类,所以这里有一些成员:

  • xs,ys 图像大小(以像素为单位)
  • p[y][x].dd 是 (x,y) 位置的像素,为 32 位整数类型
  • p[y][x].db[4] 是按色带 (r,g,b,a) 的像素访问

我也在使用自定义 OpenGL scr 和纹理分类:

  • xs,ys 缓冲区大小(以像素为单位)
  • Texture::txr 是 32 位像素指针(图像分配为线性一维数组)
  • 高度图用于存储 int 值
  • 法线贴图用于存储浮点法线向量分量

剩下要做的就是:

  1. 根据自己的喜好过滤点云
  2. 三角剖分/导出到您的打印机支持的网格

还有其他方法可以将照明编码到表面:

  1. 你可以做类似菲涅耳透镜表面

    • 因此将网格划分为段
    • 并相互偏移,使其从同一参考平面开始(z 偏移)

    这需要更少的体积/材料

    动画前半部分为正常高度编码,然后切换为菲涅耳表面编码/打包进行比较

  2. 编码照明不是高度图而是粗糙度图而不是

    • 每个像素都会被映射成小的子高度图
    • 平面是高照度/颜色强度
    • 粗糙的表面是黑色的
    • 介于两者之间的是灰色阴影

    这也可以从角度看到,并且可能相对较薄,因此需要很少的材料(比上一个子弹要少得多)

  3. 真实高度图(真实 3D 网格表示)

    您需要标准化颜色、阴影和照明伪影非常棘手,因此只剩下正常的阴影(因为表面来自单一材料、颜色、光泽度、粗糙度......),然后才提取高度图。为此,您需要很多东西,例如分割、自适应阈值化、过滤等等……最后添加空的内部并添加支撑墙,以便网格在打印时/打印后保持在一起。

【讨论】:

  • 这个答案太棒了
猜你喜欢
  • 2017-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多