P5.js 之秘密花园——自制线稿上色绘画系统
一、灵感来源
小时候大家肯定都会见过或者玩过这个,砂画
就是给一个模板上面会有轮廓,然后用给好的彩砂颜料上色,画完之后还blingbling的,也是很好看了。
大一点可能就很少有人会玩了,不过但并不代表没有这种类型的东西来替代这个,那就是秘密花园——一本探索奇境的手绘涂色书。像这种吧……
这种现在已经常常就用被来解压了。本来我是已经忘记还有这种好玩的东西的,但自从我在B站看到了一位大触的作品,被她的自己手绘然后上色的技艺折服了(她的画配色真的很漂亮)
勾起了我想上色的欲望,可是画不出很精美的线稿,无奈只能想办法做了啊,于是便有了这个想法,顺便完成了这次作业,引起舒适,哈哈哈。
二、设计思路
既然是涂色系统啦,肯定要有线稿了啊——即通过,选择一张彩色图,转为灰度图,利用其像素矩阵与设定好的Roberts算子运算,再反色,即可得到。有了线稿即相当于有了画布,那就需要画笔了,这个再processing上很容易实现,再有就是画错了,要有擦除功能,同样ez。画好之后,就可以保存下来以后留下来欣赏了,不过保存前再加点酷酷的东西那就更好不过了。ok,计划就这些,可以开始实施了。
ps:使用工具 processing + 引入部分java包
三、实施过程
1.线稿的获取
文件的读取 略
此段为图片的读取、处理及保存
public class ReadColorTest {
public int[][] getImagePixel(String image) {
int W;int H;
int[] rgb = new int[3];
File file = new File(image);
BufferedImage bi = null;
try {
bi = ImageIO.read(file);
} catch (Exception e) {
e.printStackTrace();
}
int width=bi.getWidth();
int height=bi.getHeight();
W=width;H=height;Wid=W;Heig=H;
int minx=bi.getMinX();
int miny=bi.getMinY();
double [][] gray=new double[W][H];
double [][] Gx=new double[W][H];
double [][] Gy=new double[W][H];
System.out.println("width=" + width + ",height=" + height + ".");
System.out.println("minx=" + minx + ",miniy=" + miny + ".");
for (int i = minx; i < width; i++) {
for (int j = miny; j < height; j++) {
int pixel = bi.getRGB(i, j);
rgb[0] = (pixel & 0xff0000) >> 16;
rgb[1] = (pixel & 0xff00) >> 8;
rgb[2] = (pixel & 0xff);
//zhuan hua wei hui du
gray[i][j]=(299*rgb[0]+587*rgb[1]+114*rgb[2]+ 500)/1000;
}
}
//xian gao hua chu li
int[][] newgray=new int[W][H];
for(int i=0;i<width-1;i++){
for(int j=0;j<height-1;j++){
int tmp;
Gx[i+1][j+1]=gray[i+1][j+1]-gray[i][j];
Gy[i+1][j+1]=gray[i][j+1]-gray[i+1][j];
tmp=(int)(Math.sqrt(Gx[i+1][j+1]*Gx[i+1][j+1]+Gy[i+1][j+1]*Gy[i+1][j+1]));
newgray[i+1][j+1]=tmp;
if (newgray[i+1][j+1]<0.02*255)
newgray[i+1][j+1]=0;
newgray[i+1][j+1]=255-newgray[i+1][j+1];
//println(newgray[i+1][j+1]);
}
}
//bao cun
Path=Path+"\\"+"new.png";
int[] bgr = new int[3];
try{
File out = new File(Path);
if (!out.exists())
out.createNewFile();
FileOutputStream output = new FileOutputStream(out);
BufferedImage imgOut = new BufferedImage(width, height,
BufferedImage.TYPE_BYTE_GRAY);
/*BufferedImage imgOut = new BufferedImage(width, height,
BufferedImage.TYPE_3BYTE_BGR); **/
//Graphics2D g2d = (Graphics2D) g;
Graphics2D g2d = (Graphics2D) imgOut.getGraphics();
//Graphics2D g2d = imgOut.createGraphics();
//image = g2D.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
///g2d.dispose();
g2d = imgOut.createGraphics();
g2d.setColor(new Color(255,255,255));
g2d.setStroke(new BasicStroke(1));
g2d.drawImage(imgOut,0,0,null);
g2d.dispose();
/*BufferedImage inputbig = new BufferedImage(256, 256, BufferedImage.TYPE_INT_BGR);
Graphics2D g = (Graphics2D) imgOut.getGraphics();
g.drawImage(imgOut, 255, 255,width,height,null); //画图
g.dispose();
inputbig.flush();*/
for(int i=0;i<width;i++){
for(int j=0;j<height;j++){
if(newgray[i][j]!=255){bgr[0]=0;bgr[1]=0;bgr[2]=0;}else{bgr[0]=255;bgr[1]=255;bgr[2]=255;}
imgOut.setRGB(i,j,newgray[i][j]);
}}
ImageIO.write(imgOut, "png", output);
} catch (IOException e) {
e.printStackTrace();
}
return newgray;
}}
这里我的理想状态是这样的
然鹅,现实给了我一记响亮的大嘴巴子……处理完成的图片完成就是从非洲偷渡回来的
这里是java的问题,怪自己学艺不精,图片输出后,透明化的问题一直处理不好,
背景一直就会呈现为黑色。找了很多资料,但是好像在processing上,还是没法实现。有些可惜。
2.画笔的实现
画笔的话,我没有去选择去做笔刷,毕竟涂色的话,颜色的切换和笔触粗细的切换来的舒服比较重要。所以在这里着重对画笔的面板下了些功夫。
点击鼠标右键,会弹出调色板,可比较方便的切换颜色。
本来是想用鼠标滑轮切换鼠标粗细,但是processing的mouseevent和java的mouseevent不兼容,所以只能选一个用,不过笔触的切换也是能用键盘来操纵的,也不会很麻烦。
3. 其他
擦除、保存、重置比较简单不写代码了,
大佬的装饰 见openprocessing
4.最终效果
有些惨不忍睹,哈哈哈哈……
理想状态 效果还是可以的!!!
四、总结
自己生成想上色的作品,同时画完后还可以,利用酷酷的视觉动态图片进行装饰,更有视觉冲击感。(鼠标上色,还是有点困难),ui交互方面做的比较人性化,很方便。