{"id":15893835,"url":"https://github.com/chinanf-boy/primitive-explain","last_synced_at":"2025-04-02T18:16:17.833Z","repository":{"id":90548480,"uuid":"134567624","full_name":"chinanf-boy/primitive-explain","owner":"chinanf-boy","description":"explain: primitive 用几何画画, 厉害吧, 慢来慢来❎","archived":false,"fork":false,"pushed_at":"2018-05-24T04:26:54.000Z","size":489,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-08T08:47:13.478Z","etag":null,"topics":["2d","explain","primitive","print"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chinanf-boy.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-23T12:39:15.000Z","updated_at":"2018-05-24T04:26:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"aa99d161-7a49-4ea5-8a62-5c66f3ed1452","html_url":"https://github.com/chinanf-boy/primitive-explain","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fprimitive-explain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fprimitive-explain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fprimitive-explain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fprimitive-explain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chinanf-boy","download_url":"https://codeload.github.com/chinanf-boy/primitive-explain/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246866100,"owners_count":20846496,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2d","explain","primitive","print"],"created_at":"2024-10-06T08:13:51.610Z","updated_at":"2025-04-02T18:16:17.806Z","avatar_url":"https://github.com/chinanf-boy.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# primitive [![explain](http://llever.com/explain.svg)](https://github.com/chinanf-boy/Source-Explain)\n\n「 用几何图元重现图像。 」\n\n\nExplanation\n\n\u003e \"version\": \"1.0.0\"\n\n[github source](https://github.com/fogleman/primitive)\n\n[中文](./readme.md) | ~~[english](./readme.en.md)~~\n\n\n\u003e 如果你想要运行例子, 需要下载到 `GOPATH`, 命令行不会自动生成\n\n---\n\n使用\n\n```\ngo get -u github.com/fogleman/primitive\nprimitive -i input.png -o output.png -n 100\n```\n\n| 命令 | 默认 | 详述 |\n| --- | --- | --- |\n| `i` | n/a | 输入文件|\n| `o` | n/a | 输出文件|\n| `n` | n/a | 形状数量|\n| `m` | 1 | 模式：0 =组合，1 =三角形，2 =矩形，3 =椭圆形，4 =圆形，5 =旋转，6 =贝齐尔，7 =旋转圆形，8 =多边形|\n| `rep` | 0 | 每次迭代添加N个额外形状，减少搜索次数（主要用于贝塞尔）|\n| `nth` | 1 | 保存每个第N帧（仅当输出路径中有％d时）|\n| `r` | 256 | 在处理之前将较大的输入图像调整为此大小|\n| `s` | 1024 | 输出图像大小|\n| `a` | 128 | 颜色alpha（使用`0`让算法为每个形状选择alpha）|\n| `bg` | 平均 | 开始背景颜色（十六进制）|\n| `j` | 0 | 并行工作者数量（默认使用全部核心）|\n| `v` | off | 详细输出|\n| `vv` | off | 非常详细的输出|\n\n\u003cimg src=\"https://www.michaelfogleman.com/static/primitive/examples/monalisa.3.2000.gif\" width=\"49%\"/\u003e \u003cimg src=\"https://www.michaelfogleman.com/static/primitive/examples/monalisa-original.png\" width=\"49%\"/\u003e\n\n---\n\n\u003e 既然是 命令行 , 我们从 项目的`main.go` 开始\n\n先定个示例\n\n``` bash\nprimitive -i in.png -o out.png -n 100 -v\n```\n\n### 本目录\n\n---\n\n### main.go-import\n\n__1.__ 导入\n\n\u003cdetails\u003e\n\n``` go\npackage main\n\nimport (\n\t\"flag\" // 命令行\n\t\"fmt\" // 输出\n\t\"log\" // 日志\n\t\"math/rand\" // 随机\n\t\"os\" // 平台\n\t\"path/filepath\" // 文件路径\n\t\"runtime\" // 运行-cpu数\n\t\"strconv\" // 字符串格式\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/fogleman/primitive/primitive\"\n\t\"github.com/nfnt/resize\" // 纯golang图像调整大小\n)\n\n```\n\n\u003c/details\u003e\n\n### main.go-var\n\n__2.__ 定义类型\n\n\u003cdetails\u003e\n\n``` go\nvar (\n\tInput      string\n\tOutputs    flagArray\n\tBackground string\n\tConfigs    shapeConfigArray\n\tAlpha      int\n\tInputSize  int\n\tOutputSize int\n\tMode       int\n\tWorkers    int\n\tNth        int\n\tRepeat     int\n\tV, VV      bool\n)\n\ntype flagArray []string\n\nfunc (i *flagArray) String() string {\n\treturn strings.Join(*i, \", \")\n}\n\nfunc (i *flagArray) Set(value string) error {\n\t*i = append(*i, value)\n\treturn nil\n}\n\ntype shapeConfig struct {\n\tCount  int\n\tMode   int\n\tAlpha  int\n\tRepeat int\n}\n\ntype shapeConfigArray []shapeConfig\n\nfunc (i *shapeConfigArray) String() string {\n\treturn \"\"\n}\n\nfunc (i *shapeConfigArray) Set(value string) error {\n\tn, _ := strconv.ParseInt(value, 0, 0)\n\t*i = append(*i, shapeConfig{int(n), Mode, Alpha, Repeat})\n\treturn nil\n}\n```\n\n注意 函数`Set`, 当 命令给出 `-n` 输入 100, 触发 `Set` 函数, 进而\n\n补全 `shapeConfigArray`-结构, 试试\n\n- [x] Set \n\n```\ngo run main.go -n 100\n```\n\n\n\n\u003c/details\u003e\n\n### main.go-init\n\n__3.__ 初始化 命令选项\n\n``` go\nfunc init() {\n\tflag.StringVar(\u0026Input, \"i\", \"\", \"input image path\")\n\tflag.Var(\u0026Outputs, \"o\", \"output image path\")\n\tflag.Var(\u0026Configs, \"n\", \"number of primitives\")\n\tflag.StringVar(\u0026Background, \"bg\", \"\", \"background color (hex)\")\n\tflag.IntVar(\u0026Alpha, \"a\", 128, \"alpha value\")\n\tflag.IntVar(\u0026InputSize, \"r\", 256, \"resize large input images to this size\")\n\tflag.IntVar(\u0026OutputSize, \"s\", 1024, \"output image size\")\n\tflag.IntVar(\u0026Mode, \"m\", 1, \"0=combo 1=triangle 2=rect 3=ellipse 4=circle 5=rotatedrect 6=beziers 7=rotatedellipse 8=polygon\")\n\tflag.IntVar(\u0026Workers, \"j\", 0, \"number of parallel workers (default uses all cores)\")\n\tflag.IntVar(\u0026Nth, \"nth\", 1, \"save every Nth frame (put \\\"%d\\\" in path)\")\n\tflag.IntVar(\u0026Repeat, \"rep\", 0, \"add N extra shapes per iteration with reduced search\")\n\tflag.BoolVar(\u0026V, \"v\", false, \"verbose\")\n\tflag.BoolVar(\u0026VV, \"vv\", false, \"very verbose\")\n}\n// 正如在 命令行选项 看到的 (变量 命令选项名 默认 详述)\n\nfunc errorMessage(message string) bool {   // 错误输出\n\tfmt.Fprintln(os.Stderr, message)\n\treturn false\n}\n\nfunc check(err error) { // 错误检查\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\n```\n\n\u003e init 总在 main 之前运行\n\n---\n\n### main.go-main\n\n__4.__ 重头戏\n\n```go\nfunc main() {\n\t// parse and validate arguments\n\tflag.Parse() // 解析命令行\n\tok := true\n\tif Input == \"\" {\n\t\tok = errorMessage(\"ERROR: input argument required\")\n\t}\n\tif len(Outputs) == 0 {\n\t\tok = errorMessage(\"ERROR: output argument required\")\n\t}\n\tif len(Configs) == 0 {\n\t\tok = errorMessage(\"ERROR: number argument required\")\n\t}\n\tif len(Configs) == 1 {\n\t\tConfigs[0].Mode = Mode\n\t\tConfigs[0].Alpha = Alpha\n\t\tConfigs[0].Repeat = Repeat\n\t}\n\tfor _, config := range Configs {\n\t\tif config.Count \u003c 1 {\n\t\t\tok = errorMessage(\"ERROR: number argument must be \u003e 0\")\n\t\t}\n\t}\n\tif !ok {\n\t\tfmt.Println(\"Usage: primitive [OPTIONS] -i input -o output -n count\")\n\t\tflag.PrintDefaults()\n\t\tos.Exit(1) // 错误退出\n\t}\n\n\t// 设置 日志 等级\n\tif V {\n\t\tprimitive.LogLevel = 1\n\t}\n\tif VV {\n\t\tprimitive.LogLevel = 2\n\t}\n\n\t// 设置 随机生成器\n\trand.Seed(time.Now().UTC().UnixNano())\n\n\t// determine worker count\n\tif Workers \u003c 1 {\n\t\tWorkers = runtime.NumCPU() // 拿到 CPU 核心\n\t}\n```\n\n- [x] [primitive 日志等级](./log.md)\n\n\u003e 通过简单的比大小, 确定此输出信息是否被显示\n\n---\n\n#### 4.1 读和重设图片\n\n- [x] [LoadImage](./util.md#loadimage)\n\n``` go\n\t// read input image\n\tprimitive.Log(1, \"reading %s\\n\", Input)\n\tinput, err := primitive.LoadImage(Input) // 加载图片\n\tcheck(err)\n\n\t// 如果需要缩小输入图像\n\tsize := uint(InputSize) // 256默认\n\tif size \u003e 0 {\n\t\tinput = resize.Thumbnail(size, size, input, resize.Bilinear)\n\t}\n\n```\n\n- [x] resize\n\n\u003e https://github.com/nfnt/resize\n\n\n---\n\n#### 4.2 确定背景颜色\n\n- [x] [MakeColor](./color.md#makecolor)\n\n- [x] [MakeHexColor](./color.md#makehexcolor)\n\n``` go\n\t// determine background color\n\tvar bg primitive.Color\n\tif Background == \"\" {\n\t\tbg = primitive.MakeColor(primitive.AverageImageColor(input))\n\t} else {\n\t\tbg = primitive.MakeHexColor(Background)\n    }\n    \n```\n\n- [x] [averageimagecolor](./util.md#averageimagecolor)\n\n---\n\n#### 4.3 算法\n\n- [ ] [NewModel](./model.md#newmodel)\n\n\n\n```go\n    // run algorithm\n    // 原图 颜色 输出大小 cpu数\n\tmodel := primitive.NewModel(input, bg, OutputSize, Workers)\n    primitive.Log(1, \"%d: t=%.3f, score=%.6f\\n\", 0, 0.0, model.Score)\n    \n\tstart := time.Now() // startting\n\tframe := 0\n\tfor j, config := range Configs { // 这里一般 length == 1\n\t\tprimitive.Log(1, \"count=%d, mode=%d, alpha=%d, repeat=%d\\n\",\n\t\t\tconfig.Count, config.Mode, config.Alpha, config.Repeat)\n\n\t\tfor i := 0; i \u003c config.Count; i++ { // 这里才会 循环\n\t\t\tframe++\n\n\t\t\t// 找到最佳形状并将其添加到模型中\n\t\t\tt := time.Now() // ⏰\n\n\t\t\tn := model.Step(primitive.ShapeType(config.Mode), config.Alpha, config.Repeat)\n\t\t\tnps := primitive.NumberString(float64(n) / time.Since(t).Seconds())\n\n\t\t\telapsed := time.Since(start).Seconds()  // ⏰\n\t\t\tprimitive.Log(1, \"%d: t=%.3f, score=%.6f, n=%d, n/s=%s\\n\", frame, elapsed, model.Score, n, nps)\n\n```\n\n- [ ] [Step](./model.md#step)\n\n- [ ] [NumberString](./util.md#numberstring)\n\n---\n\n#### 4.4 每次一个形状写入图片\n\n1. 确定 输出文件的路径\n\n2. 根据不同文件后缀名选择保存方式\n\n``` go\t\n\t\t\t// 接着 for i := 0; i \u003c config.Count; i++ {\n\t\t\t// write output image(s)\n\t\t\tfor _, output := range Outputs {\n\t\t\t\text := strings.ToLower(filepath.Ext(output))\n\t\t\t\tpercent := strings.Contains(output, \"%\")\n\t\t\t\tsaveFrames := percent \u0026\u0026 ext != \".gif\"\n\t\t\t\tsaveFrames = saveFrames \u0026\u0026 frame%Nth == 0\n\t\t\t\tlast := j == len(Configs)-1 \u0026\u0026 i == config.Count-1\n\t\t\t\tif saveFrames || last {\n\t\t\t\t\tpath := output\n\t\t\t\t\tif percent {\n\t\t\t\t\t\tpath = fmt.Sprintf(output, frame)\n\t\t\t\t\t} // 都是为了 做出 路径\n\t\t\t\t\tprimitive.Log(1, \"writing %s\\n\", path)\n\t\t\t\t\tswitch ext {\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcheck(fmt.Errorf(\"unrecognized file extension: %s\", ext))\n\t\t\t\t\tcase \".png\":\n\t\t\t\t\t\tcheck(primitive.SavePNG(path, model.Context.Image()))\n\t\t\t\t\tcase \".jpg\", \".jpeg\":\n\t\t\t\t\t\tcheck(primitive.SaveJPG(path, model.Context.Image(), 95))\n\t\t\t\t\tcase \".svg\":\n\t\t\t\t\t\tcheck(primitive.SaveFile(path, model.SVG()))\n\t\t\t\t\tcase \".gif\":\n\t\t\t\t\t\tframes := model.Frames(0.001)\n\t\t\t\t\t\tcheck(primitive.SaveGIFImageMagick(path, frames, 50, 250))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\n- [ ] [SavePNG](./util.md#savepng)\n- [ ] [SaveJPG](./util.md#savejpg)\n- [ ] [SaveFile](./util.md#savefile)\n- [ ] [SaveGIFImageMagick](./util.md#savegifimagemagick)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fprimitive-explain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchinanf-boy%2Fprimitive-explain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fprimitive-explain/lists"}