图片水印

图片平铺水印

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package main

import (
    "flag"
    "fmt"
    "image"
    "image/draw"
    "image/jpeg"
    "image/png"
    "log"
    "math"
    "os"
    "path/filepath"
    "strings"
)

// 配置参数
type Config struct {
    SourcePath       string  // 原图路径
    WatermarkPath    string  // 水印图路径
    OutputPath       string  // 输出图片路径
    WatermarkScale   float64 // 水印缩放比例 (0.1-1.0)
    WatermarkDensity int     // 水印密集程度 (间隔像素)
}

// 加载图片
func loadImage(filePath string) (image.Image, error) {
     file, err := os.Open(filePath)
     if err != nil {
          return nil, err
     }
     defer file.Close()

     // 优先自动识别图片格式
     img, _, err := image.Decode(file)
     if err == nil {
          return img, nil
     }

     // fallback: 用后缀判断
     ext := strings.ToLower(filepath.Ext(filePath))
     _, _ = file.Seek(0, 0) // 重置文件指针
     switch ext {
     case ".jpg", ".jpeg":
          img, err = jpeg.Decode(file)
     case ".png":
          img, err = png.Decode(file)
     default:
          return nil, fmt.Errorf("不支持的图片格式: %s", ext)
     }
     return img, err
}

// 保存图片
func saveImage(img image.Image, outputPath string) error {
    file, err := os.Create(outputPath)
    if err != nil {
        return err
    }
    defer file.Close()

    ext := strings.ToLower(filepath.Ext(outputPath))

    switch ext {
    case ".jpg", ".jpeg":
        err = jpeg.Encode(file, img, &jpeg.Options{Quality: 90})
    case ".png":
        err = png.Encode(file, img)
    default:
        return fmt.Errorf("不支持的输出格式: %s", ext)
    }

    return err
}

// 调整水印图片大小
func resizeWatermark(watermark image.Image, scale float64) image.Image {
    bounds := watermark.Bounds()
    width := int(float64(bounds.Dx()) * scale)
    height := int(float64(bounds.Dy()) * scale)

    // 确保宽高至少为1像素
    if width < 1 {
        width = 1
    }
    if height < 1 {
        height = 1
    }

    resized := image.NewRGBA(image.Rect(0, 0, width, height))

    // 简单的缩放算法
    for y := 0; y < height; y++ {
        for x := 0; x < width; x++ {
            srcX := x * bounds.Dx() / width
            srcY := y * bounds.Dy() / height
            resized.Set(x, y, watermark.At(srcX, srcY))
        }
    }

    return resized
}

// 添加平铺水印
func addTiledWatermark(src image.Image, watermark image.Image, density int) image.Image {
    srcBounds := src.Bounds()
    watermarkBounds := watermark.Bounds()

    // 创建输出图像
    result := image.NewRGBA(srcBounds)
    draw.Draw(result, srcBounds, src, image.Point{}, draw.Src)

    // 计算水印平铺的行列数
    cols := int(math.Ceil(float64(srcBounds.Dx()) / float64(watermarkBounds.Dx()+density)))
    rows := int(math.Ceil(float64(srcBounds.Dy()) / float64(watermarkBounds.Dy()+density)))

    // 平铺水印
    for row := 0; row < rows; row++ {
        for col := 0; col < cols; col++ {
            x := col * (watermarkBounds.Dx() + density)
            y := row * (watermarkBounds.Dy() + density)

            // 水印位置
            watermarkPos := image.Rect(
                x,
                y,
                x+watermarkBounds.Dx(),
                y+watermarkBounds.Dy(),
            )

            // 确保水印不超出原图范围
            if watermarkPos.Max.X > srcBounds.Max.X || watermarkPos.Max.Y > srcBounds.Max.Y {
                watermarkPos = watermarkPos.Intersect(srcBounds)
            }

            // 绘制水印
            draw.Draw(result, watermarkPos, watermark, image.Point{}, draw.Over)
        }
    }

    return result
}

func main() {
    // 解析命令行参数
    config := Config{
        SourcePath:       "/Users/xxx/Downloads/yuantu.jpg",
        WatermarkPath:    "/Users/xxx/Downloads/shuiyin.png",
        OutputPath:       "/Users/xxx/Downloads/output.png",
        WatermarkScale:   0.8,
        WatermarkDensity: 50,
    }

    // flag.StringVar(&config.SourcePath, "src", "", "原图路径 (必填)")
    // flag.StringVar(&config.WatermarkPath, "watermark", "", "水印图路径 (必填)")
    // flag.StringVar(&config.OutputPath, "output", "output.jpg", "输出图片路径")
    // flag.Float64Var(&config.WatermarkScale, "scale", 0.3, "水印缩放比例 (0.1-1.0)")
    // flag.IntVar(&config.WatermarkDensity, "density", 50, "水印密集程度 (间隔像素)")

    flag.Parse()

    // 验证必填参数
    if config.SourcePath == "" || config.WatermarkPath == "" {
        fmt.Println("使用方法:")
        fmt.Println("  watermark -src <原图路径> -watermark <水印图路径> [选项]")
        fmt.Println("\n选项:")
        flag.PrintDefaults()
        os.Exit(1)
    }

    // 验证缩放比例范围
    if config.WatermarkScale < 0.1 || config.WatermarkScale > 1.0 {
        log.Fatal("水印缩放比例必须在 0.1 到 1.0 之间")
    }

    // 验证密集程度范围
    if config.WatermarkDensity < 0 {
        log.Fatal("水印密集程度必须大于等于 0")
    }

    // 加载原图
    srcImg, err := loadImage(config.SourcePath)
    if err != nil {
        log.Fatalf("加载原图失败: %v", err)
    }

    // 加载水印图
    watermarkImg, err := loadImage(config.WatermarkPath)
    if err != nil {
        log.Fatalf("加载水印图失败: %v", err)
    }

    // 调整水印大小
    resizedWatermark := resizeWatermark(watermarkImg, config.WatermarkScale)

    // 添加平铺水印
    resultImg := addTiledWatermark(srcImg, resizedWatermark, config.WatermarkDensity)

    // 保存结果
    if err := saveImage(resultImg, config.OutputPath); err != nil {
        log.Fatalf("保存图片失败: %v", err)
    }

    fmt.Printf("水印添加成功,已保存到: %s\n", config.OutputPath)
}

视频切割

这是一个使用 ffmpeg+MoviePy 库开发的视频切割工具,可以将一个视频文件按照指定时长切割成多个片段。

安装依赖

1
2
3
4
5
brew install python
brew install ffmpeg
cd ./qiege && python3 -m venv venv # 创建虚拟环境,只需创建一次
source venv/bin/activate # 后续运行程序前都需要先激活虚拟环境,然后在虚拟环境中运行你的程序
pip install -r requirements.txt

使用方法

使用命令行运行程序,格式如下:

如果发生战乱

一、战前预警与准备阶段(若有预兆)

  1. 关注官方预警与信息渠道

    • 密切关注政府发布的战争预警、紧急通知(如乌克兰的 “空袭警报” 系统),下载官方应急 APP(如乌克兰的 “Diya” 应用)。
    • 确认本地应急管理部门(如中国的应急管理部)的联系方式和指示,避免轻信非官方谣言。
  2. 储备应急物资

寻医问药-高血压

正常的血压范围

  • 高压(收缩压)在90-140mmHg(12.0-18.7kpa)之间;
  • 低压(舒张压)在60-90mmHg(8.0-12.0kpa)之间;

脉搏

正常成人脉搏为60到100次/分,常为每分钟70-80次,平均约72次/分。老年人较慢,为55到60次/分

Go新项目启动

启用 Go Module 和 设置 Go 模块代理

包管理器 Go Modules,旨在取代工作区和 GOPATH。

1
2
3
4
5
go mod init xxx(项目名) // 初始化go.mod
go mod vendor // go get 新依赖包后,将依赖包复制到项目下的 vendor 目录。
go mod tidy // 整理现有的依赖,新增依赖包、删除缺失或者未使用的扩展包

go mod why // 这个命令用来解释为什么会依赖某个软件包,若要查看go.mod中某个间接依赖是被哪个依赖引入的,可以使用命令go mod why -m <pkg>来查看。eg: go mod why -m github.com/onsi/ginkgo【如何去掉间接依赖包?如果间接依赖包对应的源组件的新版本支持Go Modules,则升级源组件就能解决】
1
2
go env -w GO111MODULE=on
go env -w GOPRIVATE=gitlab.xxx.com # 配置公司私域代码仓库

project 依赖包升级的两种方案

AI

AI Agent(智能体)

AI Agent = LLM+Planning+Feedback+Tool use

在人工智能领域中,一个 Agent 通常指的是能够自主决策、执行任务并接收反馈的智能体。

  • LLM:语言模型(Language Model),即一种能够理解和生成自然语言的算法
  • Planning:规划能力,即 Agent 能够制定行动计划以达成目标
  • Feedback:表示 Agent 能够通过与环境互动来获取信息和调整自己的行为
  • Tool use:表示 Agent 能够使用各种工具和技术来完成任务

MCP

模型上下文协议(Model Context Protocol,简称MCP)是一种开放协议,旨在标准化应用程序向大型语言模型(Large Language Models,简称LLMs)提供上下文的方式 。它如同一个通用接口,例如计算机上的USB-C端口,使得各种AI模型能够以统一的方式连接到不同的数据源和工具 。通过提供标准化的连接方式,MCP简化了集成过程,增强了互操作性,并促进了AI应用的可扩展性 。

自媒体平台对比

自媒体平台对比

抖音

无推广播放量>=500则算合格

吃爆款形式(精致精美)

视频号

无推广播放量>=200则算合格

简单且奇葩

小红书

更适合发布笔记,而不是短视频,无推广播放量>=200则算合格

React

基础概念

  1. ECMAScript

    ECMAScript(ES)是 JavaScript 的新标准,添加了许多新的语法和功能,使 JS 代码更简洁、强大、易读。

    核心特性:使用 let 和 const 代替 var;箭头函数;async 和 await等。

mac导出微信语音

在 mac 上导出微信语音比较简单,不需要借助其他工具。

收藏语音消息

先在微信中收藏语音消息,这样文件会出现在 Favorites 目录中,便于查找。

微信导出

在收藏中找一张图片,右键选择“在 Finder 中显示”,会打开其在收藏中的目录,在同级目录中可以找到所需的语音文件(*.silk)。