实现类似于Github的随机形状、随机颜色 像素风格头像

just for fun

代码可以直接运行,所有参数都提取成了常量,几乎都可以修改(除了256色阶)

Java 版本

package graph;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 随机产生类似Github的马赛克风格头像
 * @author LLH
 */
public class GenerateMosaicHeadImg {

    /** 背景颜色 */
    private final static Color BACK_GROUND_COLOR = new Color(238, 238, 238);
    /** 图片宽 */
    private final static int IMG_WIDTH = 360;
    /** 图片高 */
    private final static int IMG_HEIGHT = 360;
    /** 图片边缘内边距 */
    private final static int PADDING = 30;
    /** 填充比率,越接近1,有色色块出现几率越高 */
    private final static double RADIO = 0.45;
    /** 每边矩形数量(建议>=5) */
    private final static int BLOCK_NUM = 9;
    /** 颜色差值评价值(越大颜色越鲜艳) */
    private final static int COLOR_DIFF_EVALUATION = 100;
    /** 基色阶数极限 */
    private final static int COLOR_LIMIT = 256;
    /** 保存路径 */
    private final static String DIR = "D://headImg/";

    public static void main(String[] args) throws IOException {
        //得到图片缓冲区
        BufferedImage bi = new BufferedImage
                (IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);

        //得到它的绘制环境(这张图片的笔)
        Graphics2D g2 = (Graphics2D) bi.getGraphics();

        //设置背景颜色
        g2.setColor(BACK_GROUND_COLOR);
        g2.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);

        // 随机颜色
        Color mainColor = getRandomColor();

        // 随机生成有效块坐标集合
        List<Point> pointList = getRandomPointList(RADIO);

        // 填充图形
        fillGraph(g2, pointList, mainColor);

        File file = new File(DIR);
        if (!file.exists()) {
            file.mkdirs();
        }

        // 输出
        ImageIO.write(bi,"JPG",
                new FileOutputStream(DIR+System.currentTimeMillis()+".jpg"));//保存图片 JPEG表示保存格式
    }

    /**
     * 填充图形
     * @param g2        画笔
     * @param pointList 填充块坐标
     * @param mainColor 填充颜色
     */
    private static void fillGraph(Graphics2D g2, List<Point> pointList, Color mainColor) {
        int rowBlockLength = (IMG_HEIGHT - 2 * PADDING) / BLOCK_NUM;
        int colBlockLength = (IMG_WIDTH - 2 * PADDING) / BLOCK_NUM;
        // 填充
        g2.setColor(mainColor);
        // 遍历points
        for (Point point : pointList) {
            g2.fillRect(PADDING + point.x * rowBlockLength,
                    PADDING + point.y * colBlockLength,
                    rowBlockLength, colBlockLength);
        }
    }

    /**
     * 获取随机颜色位置列表
     * @param radio 填充色块几率
     * @return List 列表
     */
    private static List<Point> getRandomPointList(double radio) {
        ArrayList<Point> points = new ArrayList<>();
        for (int i = 0; i < BLOCK_NUM/2; i++) {
            for (int j = 0; j < BLOCK_NUM; j++) {
                if (Math.random() < radio) {
                    points.add(new Point(i, j));
                }
            }
        }
        addReversePoints(points);
        if (BLOCK_NUM % 2 == 1) {
            for (int i = 0; i < BLOCK_NUM; i++) {
                if (Math.random() < radio) {
                    points.add(new Point(BLOCK_NUM/2, i));
                }
            }
        }
        return points;
    }

    /**
     * 获取随机颜色
     * @return Color对象
     */
    private static Color getRandomColor() {
        int r, g, b;
        do {
            r = new Random().nextInt(COLOR_LIMIT);
            g = new Random().nextInt(COLOR_LIMIT);
            b = new Random().nextInt(COLOR_LIMIT);
        } while (evaluateColor(r, g, b));
        return new Color(r, g, b);
    }

    /**
     * 评价颜色品质,只需任意两种颜色差值大于某个规定值即可
     * @return boolean
     */
    private static boolean evaluateColor(int r, int g, int b) {
        int rg = Math.abs(r - g);
        int rb = Math.abs(r - b);
        int gb = Math.abs(g - b);
        int max = rg > rb ? (rg > gb ? rg : gb) : (rb > gb ? rb : gb);
        return max < COLOR_DIFF_EVALUATION;
    }

    /**
     * 添加对称坐标
     * @param points point的列表
     */
    private static void addReversePoints(List<Point> points) {
        ArrayList<Point> pointListCopy = new ArrayList<>(points);
        for (Point point : pointListCopy) {
            points.add(new Point((BLOCK_NUM-1-point.x), point.y));
        }
    }

    /**
     * 封装了坐标的内部类
     */
    private static class Point {
        private int x;
        private int y;

        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}

效果图

实现类似于Github的随机形状、随机颜色 像素风格头像_第1张图片
实现类似于Github的随机形状、随机颜色 像素风格头像_第2张图片

Go 版本

package image

import (
	"fmt"
	"image"
	"image/color"
	"image/jpeg"
	"math"
	"math/rand"
	"os"
	"time"
)

const (
	// 图片边距
	imgSize = 360
	// 图片边缘内边距
	padding = 30
	// 填充比率
	ratio = 0.49
	// 每边矩形数量
	blockNum = 9
	// 颜色差值评价值(越大颜色越鲜艳)
	colorDiffEvaluation = 100
	// 基色阶数(默认256)
	colorLimit = 256
	// 保存路径
	dir = "D://headImg"
)

var (
	// 背景颜色
	backGroundColor = color.Gray{Y: 238}
)

// 坐标点
type point struct {
	x, y uint
}

/**
 * 主方法
 * @author LLH
 */
func NewMosaicHeadImg() (err error) {

	// 图片初始化
	img := image.NewRGBA(image.Rect(0, 0, imgSize, imgSize))
	fillBackGround(img)
	// 随机颜色
	randomColor := getRandomColor()
	// 有色坐标块集合
	pointList := generateRandomPointList()
	// 填充色块
	fillGraph(img, randomColor, pointList)
	// 写IO
	err = os.MkdirAll(dir, os.ModePerm)
	if err != nil {
		return
	}
	file, err := os.Create(fmt.Sprintf("%s/%d.jpg", dir, time.Now().UnixNano()))
	if err != nil {
		return
	}
	defer file.Close()
	err = jpeg.Encode(file, img, nil)
	fmt.Println("done!")
	return
}

// 填充色块
func fillGraph(img *image.RGBA, c *color.RGBA, points *[]point) {
	blockLen := uint(imgSize-2*padding) / blockNum
	// 填充
	for _, p := range *points {
		fillRect(img, p.x*blockLen+padding, p.y*blockLen+padding, blockLen, blockLen, c)
	}
}

// 获取随机坐标点切片
func generateRandomPointList() *[]point {
	points := make([]point, 0)
	// 获取半边随机点
	for x := 0; x < blockNum/2; x++ {
		for y := 0; y < blockNum; y++ {
			pointsGrowUp(&points, x, y)
		}
	}
	// 生成另一半的随机点
	addReversePoints(&points)
	if blockNum%2 == 1 {
		for i := 0; i < blockNum; i++ {
			pointsGrowUp(&points, blockNum/2, i)
		}
	}
	return &points
}

// 添加随机坐标
func pointsGrowUp(points *[]point, x int, y int) {
	seed := getRandSeed()
	if seed.Float64() < ratio {
		*points = append(*points, point{uint(x), uint(y)})
	}
}

// 添加对称点
func addReversePoints(points *[]point) {
	for _, p := range *points {
		*points = append(*points, point{blockNum - 1 - p.x, p.y})
	}
}

// 获取随机颜色
func getRandomColor() *color.RGBA {
	var r, g, b int16
	for {
		r = randomColorBaseValue()
		g = randomColorBaseValue()
		b = randomColorBaseValue()
		if evaluateColor(r, g, b) {
			return &color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 100}
		}
	}
}

// 获取随机颜色基色值
func randomColorBaseValue() int16 {
	seed := getRandSeed()
	return int16(seed.Int31() & (colorLimit - 1))
}

// 获取随机数种子
func getRandSeed() *rand.Rand {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	time.Sleep(time.Nanosecond)
	return seed
}

// 评价颜色鲜艳程度,足够鲜艳则返回 bool
func evaluateColor(r int16, g int16, b int16) bool {
	rg := math.Abs(float64(r - g))
	rb := math.Abs(float64(r - b))
	gb := math.Abs(float64(g - b))
	var max = math.Max(rg, rb)
	max = math.Max(max, gb)
	return max > colorDiffEvaluation
}

// 填充背景色
func fillBackGround(image *image.RGBA) {
	fillRect(image, 0, 0, imgSize, imgSize, backGroundColor)
}

// 填充矩形的颜色
func fillRect(image *image.RGBA, x0, y0 uint, width, height uint, color color.Color) {
	rgba := colorConverter(color)
	for x := x0; x < x0+width; x++ {
		for y := y0; y < y0+height; y++ {
			image.SetRGBA(int(x), int(y), rgba)
		}
	}
}

// 得到类型为RGBA的颜色
func colorConverter(c color.Color) color.RGBA {
	r, g, b, a := c.RGBA()
	return color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: uint8(a)}
}

你可能感兴趣的:(Fun)