NCF参数化建筑论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
查看: 3875|回复: 14

[个人作品] 提取图片颜色灰度,以孔的形式表现图片(c#代码)

[复制链接]
发表于 2017-4-7 18:58:52 | 显示全部楼层 |阅读模式
本帖最后由 wangjunxiong 于 2017-4-7 19:14 编辑

       利用穿孔的办法把图片的内容显示出来,在铝板行业似乎有点神秘感。GH中的Image sampler 倒是可以做到,但总感觉不能随心控制,另外也想知道这里面的工作原理,所以整理了一个代码实现这一功能。本帖子主要是想说明c#怎么提取图片颜色的灰度值。
       也许大伙都被我说懵了,先上图:
图片效果:
QQ图片20161012221600.jpg

利用孔显示的效果:
    001.png    



图片效果:
u=2627258718,1006947091&fm=23&gp=0.jpg

利用孔显示的效果:
002.png

图片效果:
u=713129213,2238654095&fm=23&gp=0.jpg

利用孔显示的效果:
003.png


以上实现的思路是依据灰度值的大小影响孔径大小。以下讲解C#代码是怎么实现的:
为使图片不失真,排孔的范围应该和图片的长宽相对应,也就是等比例缩放。


C#获取灰度图像的方法
第一种,直接调用GetPixel/SetPixel方法。
我们都知道,图像在计算机中的存在形式是位图,也即一个矩形点阵,每一个点被称为一个像素。在这种方法中,我们通过GDI+中提供的GetPixel方法来读取像素的颜色,并加以计算,然后再使用SetPixel方法将计算后的颜色值应用到相应的像素上去,这样便可以得到灰度图像。
上边提到的“计算”便是指得到灰度图像的计算,其公式是:
r = (像素点的红色分量 + 像素点的绿色分量 + 像素点的蓝色分量) / 3
最后得到的r便是需要应用到原像素点的值。具体的编程实现也非常的简单,只需要遍历位图的每一个像素点,然后使用SetPixel方法将上边计算得到的值应用回去即可。


以上文字源自度娘。

如果图片是800*800大小,我们可以把排孔的Rectangle设置为4000*4000 (放大5倍或者其它倍数),另外考虑到铝板本身需要一个无孔偏移,可以设定到孔的边缘为20mm,那么实际大小为4020*4020 ,这个就是铝板的尺寸,而排孔的尺寸仍然是4000*4000 ;那为什么需要放大呢,因为像素相对而言是很小的,也可以理解为一个挨着一个无间隙排列的,放大的目的是拉开像素点之间的距离,使之有间隙。800*800的像数可以理解为长度方向和宽度方向各有800个像素点,4000/800=5 ,这个就是间隙,如果间隙过小,而又不想放大倍数,那么只能考虑多个像素点跳选,或是2的倍数或是3的倍数,当倍数越大排孔显示越模糊。
代码:
double imageWidth = (double)imageForPunching.Width;
double imageHeight = (double)imageForPunching.Height;


Rhino.Geometry.Rectangle3d rec = new Rectangle3d(new Plane(localPoint, Vector3d.XAxis, Vector3d.YAxis), imageWidth * zoom + offSet * 2, imageHeight * zoom + offSet * 2);
Rhino.Geometry.Rectangle3d inRec = new Rectangle3d(new Plane(new Point3d(localPoint.X + offSet, localPoint.Y + offSet, 0), Vector3d.XAxis, Vector3d.YAxis), imageWidth * zoom, imageHeight * zoom);
Line lineX = new Line(new Point3d(localPoint.X + offSet, localPoint.Y + offSet + imageHeight * zoom, 0), Vector3d.XAxis, imageWidth * zoom);//等分点使用用曲线
Line lineY = new Line(new Point3d(localPoint.X + offSet, localPoint.Y + offSet + imageHeight * zoom, 0), -Vector3d.YAxis, imageHeight * zoom);//等分点使用用曲线


outCurveList.Add(rec.ToNurbsCurve());
outCurveList.Add(inRec.ToNurbsCurve());


double lineXLength = lineX.Length;
double lineYLength = lineY.Length;
int xCount = (int)(lineXLength / hDist);//X方向的数量计算
int yCount = (int)(lineYLength / vDist);//Y方向的数量计算
double imageWidthDist = imageWidth / (xCount + 1);//也就是通过x方向的数量求得ImageWidth的间隔。
double imageHeightDist = imageHeight / (yCount + 1);//也就是通过Y方向的数量求得ImageHeight的间隔。


List<double> varRadList = new List<double>();
List<Point3d> endPointList = new List<Point3d>();
Color currentColor;
int r;
double db;
Bitmap currentBitmap = new Bitmap(imageForPunching);//图片转换


for (int i = 0; i < xCount + 1; i++) //循环取值,注意使用长度对象
{
                for (int j = 0; j < yCount + 1; j++)
                {
                    int xint, yint;
                    xint = (int)(i * imageWidthDist);//像素为整数,但是间隔的倍数不一定是整数,所以要转换。
                    yint = (int)(j * imageHeightDist);


                    if (xint > imageForPunching.Width)//做一个判断。
                    {
                        xint = imageForPunching.Width;
                    }
                    if (yint > imageForPunching.Width)
                    {
                        yint = imageForPunching.Height;
                    }


                    currentColor = currentBitmap.GetPixel(xint, yint);//颜色取值的核心函数
                    r =(currentColor.R + currentColor.G + currentColor.B) / 3;//取灰度
                    db = maxiRad / 255 * r;//颜色取值为0到255,转换为相应的直径值。


                    if (db < miniRad)
                    {
                        varRadList.Add(miniRad);//转换为直径   
                    }
                    else
                    {
                        varRadList.Add(maxiRad / 255 * r);//转换为直径     
                    }
                }
}//得到直径集合。


Point3d[] outPointX;
Rhino.Geometry.LineCurve lineCurveX = new LineCurve(lineX);
lineCurveX.DivideByCount(xCount, true, out outPointX);//X方向等分返回点
foreach (Point3d point in outPointX)
  {
                Rhino.Geometry.Line line = new Rhino.Geometry.Line(point, -Vector3d.YAxis, lineYLength);
                Rhino.Geometry.LineCurve lineCurve = new Rhino.Geometry.LineCurve(line);
                Point3d[] outPoints;
                lineCurve.DivideByCount(yCount, true, out outPoints); //Y方向等分返回点


                foreach (Point3d outPoint in outPoints)
                {
                    endPointList.Add(outPoint);//输出点收集
                }
}


  if (endPointList.Count != varRadList.Count)
{
                RhinoApp.WriteLine(endPointList.Count.ToString());
                RhinoApp.WriteLine(varRadList.Count.ToString());
                Rhino.RhinoApp.WriteLine("半径的数量和画圆的数量对不上!");
                circleList.Clear();
                return;
}//做一个判断,理论上不会出现的。


for (int i = 0; i < endPointList.Count; i++)
{
                Rhino.Geometry.Circle circle = new Circle(Plane.WorldXY, endPointList, varRadList / 2);
                circleList.Add(circle);
                endDisplay.AddCircle(circle, System.Drawing.Color.Aqua);
}
endDisplay.AddCurve(rec.ToNurbsCurve());
endDisplay.AddCurve(inRec.ToNurbsCurve());


至此,代码已完成。
输入参数如下图:
004.png

上个美女的图片结束这个帖子:
图片
u=4030640066,797390090&amp;fm=23&amp;gp=0.jpg

排孔图:
005.png

放大局部:
006.png


ps:做这个代码的时候,一直卡在循环环节,孔直径和孔数量对应不上,主要是把循环次数的对象搞错。
       另外,我自己做了个UI,所以没依附在GH中,但原理是一样的。


评分

参与人数 2强度 +13 照度 +70 收起 理由
skywoolf + 10 + 50 感谢分享!
月之眼 + 3 + 20 很有启发!

查看全部评分

发表于 2017-4-7 23:29:11 | 显示全部楼层
好久没看到这么经典的帖子了
发表于 2017-4-8 10:40:28 | 显示全部楼层
赞!好久不见果然是厚积薄发!
发表于 2017-4-8 18:40:37 | 显示全部楼层
好好学习下,孔太密集电脑玩不动
发表于 2017-4-10 16:07:27 | 显示全部楼层
确实很好的一股清流~
发表于 2017-5-19 14:06:27 | 显示全部楼层
nice biancheng
发表于 2017-6-29 18:59:24 | 显示全部楼层
66666666666
发表于 2017-8-18 18:12:19 | 显示全部楼层
代码是一直的痛处,希望大大们能给予学习之路的指导~

点评

耐得住寂寞,沉得住气,端起书看c#基础,然后.......... 先把这步做了,否则就没有然后了......  详情 回复 发表于 2017-8-19 12:06
 楼主| 发表于 2017-8-19 12:06:25 | 显示全部楼层
archsamxian 发表于 2017-8-18 18:12
代码是一直的痛处,希望大大们能给予学习之路的指导~

耐得住寂寞,沉得住气,端起书看c#基础,然后.......... 先把这步做了,否则就没有然后了......
发表于 2017-10-6 18:22:57 | 显示全部楼层
大佬好!请问这个代码怎么用啊?是rhinoscript,还是grasshopper 的电池?

点评

可以用GH中的C#实现。rhinoscript是VB言语,不能搞混了哦。  详情 回复 发表于 2017-10-15 19:09
 楼主| 发表于 2017-10-15 19:09:30 | 显示全部楼层
展颜 发表于 2017-10-6 18:22
大佬好!请问这个代码怎么用啊?是rhinoscript,还是grasshopper 的电池?

可以用GH中的C#实现。rhinoscript是VB言语,不能搞混了哦。

点评

谢谢!帖子里的那种用户界面,应该没有贴出来代码吧?贴出来的是核心运算代码  详情 回复 发表于 2017-10-23 20:07
发表于 2017-10-23 20:07:20 | 显示全部楼层
wangjunxiong 发表于 2017-10-15 19:09
可以用GH中的C#实现。rhinoscript是VB言语,不能搞混了哦。

谢谢!帖子里的那种用户界面,应该没有贴出来代码吧?贴出来的是核心运算代码

点评

用户界面是一堆类文件。  详情 回复 发表于 2017-11-9 09:10
 楼主| 发表于 2017-11-9 09:10:47 | 显示全部楼层
展颜 发表于 2017-10-23 20:07
谢谢!帖子里的那种用户界面,应该没有贴出来代码吧?贴出来的是核心运算代码

用户界面是一堆类文件。
发表于 4 天前 | 显示全部楼层
怎么样呀,可不可以操作一下
发表于 4 天前 | 显示全部楼层
怎么样可以联系你!急急急

小黑屋|手机版|NCF参数化建筑论坛 ( 辽ICP备12011358号-1   辽公网安备 21020302000097号

GMT+8, 2018-4-20 10:58 , Processed in 0.258405 second(s), 27 queries , Gzip On.

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表