|
楼主 |
发表于 2021-11-4 09:32:34
|
显示全部楼层
仅使用Pil库进行图片处理,r通道高斯模糊设置模糊半径,g通道边缘检测并设置阈值0.5,b通道根据g通道计算闭合区域,并对闭合区域咦其中心y值(纹理坐标系)填色。
import os
from PIL import Image
from PIL import ImageFilter
dx=[-1,0,0,1]
dy=[0,-1,1,0]
xmin,ymin,xmax,ymax=[],[],[],[]
#思路:用PIL内置filter进行高斯模糊解决第一问
#用PIL内置filter进行边缘检测,遍历图片对每个纹素进行阈值判断解决第二问
#第三问:在边缘检测后的二值化图像求取闭合区域,通过深度优先搜索遍历黑色区域(非边缘部分)
#对每个连通分量进行标记,然后记录xmin,xmax,ymin,ymax得到连通块的bbox求取中心坐标
#栈模拟dfs防止溢出得不到结果,因为离线操作不太考虑时间效率。
class node:
def __init__(self,x,y):
self.x=x
self.y=y
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
def clear(self):
self.items = []
#栈模拟dfs
def dfs(x,y,label,img):
while(s.size()>0):
cur=s.peek()
flag[cur.x][cur.y]=label
xmin[label-1]=min(xmin[label-1],cur.x)
ymin[label-1]=min(ymin[label-1],cur.y)
xmax[label-1]=max(xmax[label-1],cur.x)
ymax[label-1]=max(ymax[label-1],cur.y)
s.pop()
for i in range(4):
next=node(cur.x+dx,cur.y+dy)
if(next.x<0 or next.y <0 or next.x>=width or next.y >=height): continue
if(img.getpixel((next.x,next.y))==0 and flag[next.x][next.y]==0 ):
s.push(next)
def threshold(t,img):
for w in range(0,width):
for h in range(0,height):
pixel=float(img.getpixel((w,h)))/255
if (pixel>t):
img.putpixel((w,h),255)
else:
img.putpixel((w,h),0)
#记录相应的连通块及对应的bbox
def connected(img):
set=1
xmin.append(width)
ymin.append(height)
xmax.append(0)
ymax.append(0)
for w in range(0,width):
for h in range(0,height):
if(img.getpixel((w,h))==0 and flag[w][h]==0):
s.push(node(w,h))
xmin.append(width)
ymin.append(height)
xmax.append(0)
ymax.append(0)
dfs(w,h,set,img)
set+=1
#按bbox得到的中心坐标y值对其连通块填充颜色
def fill(img):
for w in range(0,width):
for h in range(0,height):
if(flag[w][h]>0):
num=flag[w][h]-1
cy=ymin[num]+(ymax[num]-ymin[num])/2
cy=cy/height
cy=int(cy*255)
img.putpixel((w,h),cy)
if __name__ == '__main__':
imgPath="D:/testD/F"
newPath="D:/testD/F_n/"
files=os.listdir(imgPath)
for file in files:
im=Image.open(imgPath+'/'+file)
width=im.size[0]
height=im.size[1]
r,g,b,a=im.split()
#第一问
r=r.filter(ImageFilter.GaussianBlur(radius=3))
g=g.filter(ImageFilter.FIND_EDGES)
flag=[[0 for j in range(height)] for i in range(width)]
s=Stack()
#第二问
threshold(0.5,g)
b=g.copy()
#第三问
connected(b)
fill(b)
img=Image.merge("RGBA",[r,g,b,a])
img.save(newPath+'/'+file,'png')
给定人物模型制作运动动画并进行物理渲染的线框效果展示。
制作思路:https://www.bilibili.com/read/cv3168157
TIM图片20191025140102.png-64kB
Shader "TestC/wireframe"
{
Properties
{
[Toggle] _NoTriangle("NoTriangle",float)=0
_wireThickness("wire thickness",Range(0,5))=0.05
_wireColor("wire color",Color)=(1,1,1,1)
_wireSmoothing("wire smoothing",Range(0,5))=1
}
SubShader
{
CGINCLUDE
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _wireColor;
half _wireThickness;
half _wireSmoothing;
struct a2v{
float4 vertexOSITION;
};
struct v2g
{
float3 vpos:TEXCOORD0;
float4 wpos : SV_POSITION;
};
struct g2f
{
float3 vpos:TEXCOORD1;
float4 pos : SV_POSITION;
float3 barycentric:TEXCOORD0;
float3 worldNormal:TEXCOORD2;
};
v2g vert (a2v v)
{
v2g o;
o.vpos=v.vertex.xyz;
o.wpos = mul(unity_ObjectToWorld,v.vertex);
return o;
}
float3 getNormal(float3 pos0, float3 pos1,float3 pos2)
{
float3 a=pos1-pos0;
float b=pos2-pos0;
return normalize(cross(a,b));
}
[maxvertexcount(3)]
void geom(triangle v2g p[3],inout TriangleStream<g2f> stream)
{
g2f o1,o2,o3;
float3 param=float3(0,0,0);
#if _NOTRIANGLE_ON
float EdgeA = length(p[0].vpos - p[1].vpos);
float EdgeB = length(p[1].vpos - p[2].vpos);
float EdgeC = length(p[2].vpos - p[0].vpos);
if(EdgeA > EdgeB && EdgeA > EdgeC)
param.y = 1.;
else if (EdgeB > EdgeC && EdgeB > EdgeA)
param.x = 1.;
else
param.z = 1.;
#endif
o1.pos=mul(UNITY_MATRIX_VP,p[0].wpos);
o2.pos=mul(UNITY_MATRIX_VP,p[1].wpos);
o3.pos=mul(UNITY_MATRIX_VP,p[2].wpos);
float3 triangleNomral=getNormal(p[0].wpos,p[1].wpos,p[2].wpos);
o1.worldNormal=triangleNomral;
o2.worldNormal=triangleNomral;
o3.worldNormal=triangleNomral;
o1.barycentric=float3(1,0,0)+param;
o2.barycentric=float3(0,0,1)+param;
o3.barycentric=float3(0,1,0)+param;
o1.vpos=p[0].vpos;
o2.vpos=p[1].vpos;
o3.vpos=p[2].vpos;
stream.Append(o1);
stream.Append(o2);
stream.Append(o3);
stream.RestartStrip();
}
inline float aa1 (float threshold, float dist) {
float delta = fwidth(dist) * _wireSmoothing;
threshold=threshold*delta;
return smoothstep(threshold-delta, threshold+delta, dist);
}
fixed4 fragfront (g2f i) : SV_Target
{
i.worldNormal=normalize(i.worldNormal);
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldlight_dir=normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse=_LightColor0.rgb*_wireColor.rgb*saturate(dot(normalize(i.worldNormal),worldlight_dir));
float3 barycentric=float3(i.barycentric.x,i.barycentric.y,i.barycentric.z);
float thickness=_wireThickness;
float d=min(barycentric.x,min(barycentric.y,barycentric.z));
float t=1-aa1(thickness,d);
return fixed4(ambient+diffuse,t);
}
ENDCG
Pass
{
Tags{"Queue"="AlphaTest" "LightMode"="ForwardBase"}
Cull front
AlphaToMask On
CGPROGRAM
#pragma shader_feature _NOTRIANGLE_ON
#pragma vertex vert
#pragma fragment fragfront
#pragma geometry geom
#pragma target 4.0
ENDCG
}
Pass
{
Tags{"Queue"="AlphaTest" "LightMode"="ForwardBase"}
Cull back
AlphaToMask On
CGPROGRAM
#pragma shader_feature _NOTRIANGLE_ON
#pragma vertex vert
#pragma fragment fragfront
#pragma geometry geom
#pragma target 4.0
ENDCG
}
}
FallBack "Diffuse"
} |
|