手机网站html5,福州市住房和城乡建设网站,wordpress分类目录和导航栏,四川平昌县建设局网站加水印应该是个很常见的需求#xff0c;但是网上找的代码#xff0c;都感觉不太完善。记录下自己搞出来的一个方案
水印有几个需求#xff1a;
中文文字水印文字倾斜满图都是#xff0c;而不是只有一个地方水印文字所在之处完全展示水印
实现思路
准备水印图
我是这么…加水印应该是个很常见的需求但是网上找的代码都感觉不太完善。记录下自己搞出来的一个方案
水印有几个需求
中文文字水印文字倾斜满图都是而不是只有一个地方水印文字所在之处完全展示水印
实现思路
准备水印图
我是这么做的先手工生成一张水印图尺寸比较小约100*100。然后也把文字旋转一定角度当然如果想做随机角度也是可以的代码再复杂点处理就好不影响这个思路生成这个一张图片并且底色选择纯黑。
然后用opencv将水印图处理出一张二值图
这个也不难底色纯黑这个很好做用Threshold很容易实现。
然后用水印图和原图叠加
核心就是要用copyTo()然后要输入mask二值图的作用就是做掩码这样就可以实现完美的水印添加效果。
其他细节
因为水印图通常是远小于原图的其实也可以采用其他方案。总之核心就是要有水印图和对应的二值图如何实现满图添加水印呢
思路
先设置一个水印的间距比如两百个像素点然后通过水印图的大小和间距的值计算生成一个尺寸大于等于原图的纯水印图同时也要有二值图然后裁剪成和原图一样大然后用copyTo()叠加即可。
这样性能很高go语言实现处理一张图片不超过10ms。
go语言实现
思路都是通用的基于opencv都能实现。 func ImgWatermark(img gocv.Mat) {var Watermark gocv.Matvar WatermarkBW gocv.Mat// 不同通道的图片使用不同的水印if img.Channels() 3 {// 判断是否需要初始化if g.Watermark3 nil { // g. 是我设置的全局变量因为不需要每次都加载水印。加载一次到内存即可mat : gocv.IMRead(watermark.jpg, gocv.IMReadColor)g.Watermark3 mat// 取得水印图片的二值图watermarkGray3 : gocv.NewMat()gocv.CvtColor(*g.Watermark3, watermarkGray3, gocv.ColorBGRToGray)// 取得二值图的黑白图BW3 : gocv.NewMat()g.WatermarkBW3 BW3gocv.Threshold(watermarkGray3, g.WatermarkBW3, 30, 255, gocv.ThresholdBinary)watermarkGray3.Close()}Watermark *g.Watermark3WatermarkBW *g.WatermarkBW3} else if img.Channels() 4 {// 判断是否需要初始化if g.Watermark4 nil {mat : gocv.IMRead(watermark.jpg, gocv.IMReadColor)g.Watermark4 matgocv.Merge([]gocv.Mat{mat, gocv.NewMatWithSizeFromScalar(gocv.NewScalar(255, 255, 255, 255), mat.Rows(), mat.Cols(), gocv.MatTypeCV8UC1)}, g.Watermark4)// 取得水印图片的二值图watermarkGray4 : gocv.NewMat()gocv.CvtColor(*g.Watermark4, watermarkGray4, gocv.ColorBGRToGray)// 取得二值图的黑白图BW4 : gocv.NewMat()g.WatermarkBW4 BW4gocv.Threshold(watermarkGray4, g.WatermarkBW4, 30, 255, gocv.ThresholdBinary)watermarkGray4.Close()}Watermark *g.Watermark4WatermarkBW *g.WatermarkBW4} else if img.Channels() 1 {// 判断是否需要初始化if g.Watermark1 nil {mat : gocv.IMRead(watermark.jpg, gocv.IMReadGrayScale) // 直接读取黑白g.Watermark1 mat// 取得二值图的黑白图BW1 : gocv.NewMat()g.WatermarkBW1 BW1gocv.Threshold(mat, g.WatermarkBW1, 30, 255, gocv.ThresholdBinary)}Watermark *g.Watermark1WatermarkBW *g.WatermarkBW1} else {fmt.Println(图片通道数不支持, img.Channels())return}waterCol : Watermark.Cols()waterRow : Watermark.Rows()// 为了水印不要那么密这里设置一个间距spacing : 200 // 水印的间距waterColAndSpacing : waterCol spacingwatreRowAndSpacing : waterRow spacing// 计算需要添加水印的次数watermarkHugeRowTimes : int(math.Ceil(float64(img.Rows()) / float64(watreRowAndSpacing))) //向上取整watermarkHugeColTimes : int(math.Ceil(float64(img.Cols()) / float64(waterColAndSpacing)))imgRect : image.Rectangle{} //每次取图像中哪一块区域的数据结构var croppedMat gocv.Mat // 每次从原图像上裁剪和水印图像一样大小的一块newWater : WatermarknewWaterBW : WatermarkBW// 循环一块块的给图像添加水印for i : 0; i watermarkHugeColTimes; i {imgRect.Min.X i * waterColAndSpacingimgRect.Max.X imgRect.Min.X waterColif imgRect.Max.X img.Cols() { // 判断是否超出原图像的列数imgRect.Max.X img.Cols()}for j : 0; j watermarkHugeRowTimes; j {imgRect.Min.Y j * watreRowAndSpacingimgRect.Max.Y imgRect.Min.Y waterRowif imgRect.Max.Y img.Rows() { // 判断是否超出原图像的行数imgRect.Max.Y img.Rows()}croppedMat img.Region(imgRect) // 原图像中一块区域的浅拷贝修改它会连带修改原图像// 判断裁剪的图像大小是否与水印图像大小一致不一致则需要重新裁剪if croppedMat.Rows() ! Watermark.Rows() || croppedMat.Cols() ! Watermark.Cols() {newRect : image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: croppedMat.Cols(), Y: croppedMat.Rows()}}newWater Watermark.Region(newRect)newWaterBW WatermarkBW.Region(newRect)}newWater.CopyToWithMask(croppedMat, newWaterBW) // 用水印图覆盖原图像}}
}