|
7楼
发表于 2025-6-15 10:02:18
|
只看该作者
浙江省湖州市
### 图片去水印源码实现方案
基于颜色选择的去水印原理是识别特定颜色范围内的像素,然后通过周围像素进行填充修复。以下提供多种语言的实现方案,包括图片和PDF去水印的不同处理方式。
### 一、Python实现方案(图片去水印)
Python的PIL和OpenCV库非常适合实现基于颜色的去水印功能:
```python
import cv2
import numpy as np
from PIL import Image, ImageDraw
def remove_watermark_by_color(image_path, output_path, color_ranges, radius=5):
"""
通过颜色范围去除图片水印
image_path: 输入图片路径
color_ranges: 水印颜色范围列表,每个元素为[(min_b, min_g, min_r), (max_b, max_g, max_r)]
radius: 修复半径
"""
# 读取图片
img = cv2.imread(image_path)
if img is None:
print(f"无法读取图片: {image_path}")
return False
# 转换为HSV颜色空间(更适合颜色识别)
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 创建掩码
mask = np.zeros_like(hsv_img[:, :, 0])
for color_range in color_ranges:
min_color, max_color = color_range
# 生成当前颜色范围的掩码
current_mask = cv2.inRange(hsv_img, np.array(min_color), np.array(max_color))
mask = cv2.bitwise_or(mask, current_mask)
# 寻找水印区域轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 使用修复算法去除水印
result = cv2.inpaint(img, mask, radius, cv2.INPAINT_NS)
# 保存结果
cv2.imwrite(output_path, result)
return True
def select_watermark_color(image_path):
"""交互式选择水印颜色范围"""
img = cv2.imread(image_path)
if img is None:
print(f"无法读取图片: {image_path}")
return []
# 显示图片并允许用户点击选择颜色
cv2.imshow("选择水印颜色,点击完成后按ESC退出", img)
color_points = []
def mouse_callback(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
# 获取点击处的BGR颜色
b, g, r = img[y, x]
color_points.append((b, g, r))
print(f"选择颜色: B={b}, G={g}, R={r}")
cv2.setMouseCallback("选择水印颜色,点击完成后按ESC退出", mouse_callback)
while True:
if cv2.waitKey(1) == 27: # ESC键退出
break
cv2.destroyAllWindows()
# 生成颜色范围(以选择颜色为中心,上下浮动20)
color_ranges = []
for b, g, r in color_points:
min_color = (max(0, b-20), max(0, g-20), max(0, r-20))
max_color = (min(255, b+20), min(255, g+20), min(255, r+20))
color_ranges.append((min_color, max_color))
return color_ranges
# 使用示例
if __name__ == "__main__":
image_path = "watermarked_image.jpg"
output_path = "watermark_removed.jpg"
# 交互式选择颜色
color_ranges = select_watermark_color(image_path)
if color_ranges:
# 去除水印
success = remove_watermark_by_color(image_path, output_path, color_ranges)
if success:
print(f"水印去除完成,结果保存至: {output_path}")
else:
print("水印去除失败")
```
### 二、Java实现方案(图片去水印)
使用Java的OpenCV绑定库实现类似功能:
```java
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
public class WatermarkRemover {
static {
// 加载OpenCV库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
String imagePath = "watermarked_image.jpg";
String outputPath = "watermark_removed.jpg";
try {
// 交互式选择颜色
List<ColorRange> colorRanges = selectWatermarkColor(imagePath);
if (!colorRanges.isEmpty()) {
// 去除水印
boolean success = removeWatermarkByColor(imagePath, outputPath, colorRanges);
if (success) {
System.out.println("水印去除完成,结果保存至: " + outputPath);
} else {
System.out.println("水印去除失败");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
static List<ColorRange> selectWatermarkColor(String imagePath) {
Mat image = Imgcodecs.imread(imagePath);
if (image.empty()) {
System.out.println("无法读取图片: " + imagePath);
return new ArrayList<>();
}
// 转换为AWT图像用于显示
MatOfByte matOfByte = new MatOfByte();
Imgcodecs.imencode(".jpg", image, matOfByte);
byte[] byteArray = matOfByte.toArray();
ImageIcon icon = new ImageIcon(byteArray);
// 创建窗口
JFrame frame = new JFrame("选择水印颜色,点击完成后关闭窗口");
frame.setSize(icon.getIconWidth(), icon.getIconHeight() + 50);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JLabel label = new JLabel(icon);
frame.add(label);
final List<ColorRange> colorRanges = new ArrayList<>();
label.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// 获取点击处的BGR颜色
int x = e.getX();
int y = e.getY();
if (x >= 0 && x < image.cols() && y >= 0 && y < image.rows()) {
double[] pixel = image.get(y, x);
int b = (int) pixel[0];
int g = (int) pixel[1];
int r = (int) pixel[2];
System.out.println("选择颜色: B=" + b + ", G=" + g + ", R=" + r);
// 生成颜色范围(上下浮动20)
int minB = Math.max(0, b - 20);
int minG = Math.max(0, g - 20);
int minR = Math.max(0, r - 20);
int maxB = Math.min(255, b + 20);
int maxG = Math.min(255, g + 20);
int maxR = Math.min(255, r + 20);
colorRanges.add(new ColorRange(new Scalar(minB, minG, minR), new Scalar(maxB, maxG, maxR)));
}
}
});
frame.setVisible(true);
// 等待窗口关闭
try {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
return colorRanges;
}
static boolean removeWatermarkByColor(String imagePath, String outputPath, List<ColorRange> colorRanges) {
Mat image = Imgcodecs.imread(imagePath);
if (image.empty()) {
System.out.println("无法读取图片: " + imagePath);
return false;
}
// 转换为HSV颜色空间
Mat hsvImage = new Mat();
Imgproc.cvtColor(image, hsvImage, Imgproc.COLOR_BGR2HSV);
// 创建掩码
Mat mask = Mat.zeros(image.size(), CvType.CV_8UC1);
for (ColorRange range : colorRanges) {
Mat currentMask = new Mat();
Core.inRange(hsvImage, range.minColor, range.maxColor, currentMask);
Core.bitwise_or(mask, currentMask, mask);
}
// 寻找轮廓并填充
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(mask, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// 使用修复算法去除水印
Mat result = new Mat();
Imgproc.inpaint(image, mask, result, 5, Imgproc.INPAINT_NS);
// 保存结果
return Imgcodecs.imwrite(outputPath, result);
}
static class ColorRange {
Scalar minColor;
Scalar maxColor;
ColorRange(Scalar minColor, Scalar maxColor) {
this.minColor = minColor;
this.maxColor = maxColor;
}
}
}
```
### 三、C++实现方案(图片去水印)
使用C++和OpenCV实现高性能去水印:
```cpp
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
#include <string>
struct ColorRange {
cv::Scalar minColor;
cv::Scalar maxColor;
ColorRange(cv::Scalar min, cv::Scalar max) : minColor(min), maxColor(max) {}
};
std::vector<ColorRange> selectWatermarkColor(const std::string& imagePath) {
cv::Mat image = cv::imread(imagePath);
if (image.empty()) {
std::cerr << "无法读取图片: " << imagePath << std::endl;
return {};
}
cv::namedWindow("选择水印颜色,点击完成后按ESC退出", cv::WINDOW_NORMAL);
cv::imshow("选择水印颜色,点击完成后按ESC退出", image);
std::vector<ColorRange> colorRanges;
auto mouseCallback = [&](int event, int x, int y, int flags, void* userdata) {
if (event == cv::EVENT_LBUTTONDOWN) {
if (x >= 0 && x < image.cols && y >= 0 && y < image.rows) {
cv::Vec3b pixel = image.at<cv::Vec3b>(y, x);
int b = pixel[0];
int g = pixel[1];
int r = pixel[2];
std::cout << "选择颜色: B=" << (int)b << ", G=" << (int)g << ", R=" << (int)r << std::endl;
// 生成颜色范围(上下浮动20)
int minB = std::max(0, b - 20);
int minG = std::max(0, g - 20);
int minR = std::max(0, r - 20);
int maxB = std::min(255, b + 20);
int maxG = std::min(255, g + 20);
int maxR = std::min(255, r + 20);
colorRanges.emplace_back(cv::Scalar(minB, minG, minR), cv::Scalar(maxB, maxG, maxR));
}
}
};
cv::setMouseCallback("选择水印颜色,点击完成后按ESC退出", mouseCallback);
while (true) {
if (cv::waitKey(1) == 27) { // ESC键退出
break;
}
}
cv::destroyAllWindows();
return colorRanges;
}
bool removeWatermarkByColor(const std::string& imagePath, const std::string& outputPath, const std::vector<ColorRange>& colorRanges) {
cv::Mat image = cv::imread(imagePath);
if (image.empty()) {
std::cerr << "无法读取图片: " << imagePath << std::endl;
return false;
}
// 转换为HSV颜色空间
cv::Mat hsvImage;
cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);
// 创建掩码
cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1);
for (const auto& range : colorRanges) {
cv::Mat currentMask;
cv::inRange(hsvImage, range.minColor, range.maxColor, currentMask);
cv::bitwise_or(mask, currentMask, mask);
}
// 寻找轮廓
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(mask, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
// 使用修复算法去除水印
cv::Mat result;
cv::inpaint(image, mask, result, 5, cv::INPAINT_NS);
// 保存结果
return cv::imwrite(outputPath, result);
}
int main() {
std::string imagePath = "watermarked_image.jpg";
std::string outputPath = "watermark_removed.jpg";
// 交互式选择颜色
std::vector<ColorRange> colorRanges = selectWatermarkColor(imagePath);
if (!colorRanges.empty()) {
// 去除水印
bool success = removeWatermarkByColor(imagePath, outputPath, colorRanges);
if (success) {
std::cout << "水印去除完成,结果保存至: " << outputPath << std::endl;
} else {
std::cout << "水印去除失败" << std::endl;
}
}
return 0;
}
```
### 四、PDF去水印方案(Python)
PDF去水印与图片不同,需要处理矢量图形,以下是基于PyMuPDF的实现:
```python
import fitz # PyMuPDF
def remove_watermark_from_pdf(input_path, output_path, watermark_color_ranges=None):
"""
从PDF中去除水印
input_path: 输入PDF路径
output_path: 输出PDF路径
watermark_color_ranges: 水印颜色范围,未提供时尝试自动检测
"""
doc = fitz.open(input_path)
for page in doc:
# 获取页面中的所有对象
for inst in page.get_images():
xref = inst[0]
image = doc.extract_image(xref)
image_bytes = image["image"]
# 转换为PIL图像进行颜色检测
from PIL import Image
from io import BytesIO
img = Image.open(BytesIO(image_bytes))
img = img.convert("RGB")
# 检查是否为水印(简化判断:假设水印颜色较浅且重复出现)
is_watermark = False
if watermark_color_ranges:
# 根据颜色范围判断
width, height = img.size
pixel_count = 0
for y in range(height):
for x in range(width):
r, g, b = img.getpixel((x, y))
for color_range in watermark_color_ranges:
min_r, min_g, min_b = color_range[0]
max_r, max_g, max_b = color_range[1]
if min_r <= r <= max_r and min_g <= g <= max_g and min_b <= b <= max_b:
pixel_count += 1
break
# 如果大部分像素属于水印颜色范围,则认为是水印
if pixel_count > width * height * 0.3:
is_watermark = True
else:
# 自动检测(简化:亮度高于阈值且颜色单一)
colors = img.getdata()
light_pixels = sum(1 for r, g, b in colors if (r + g + b) / 3 > 200)
if light_pixels > len(colors) * 0.5:
is_watermark = True
# 如果是水印,则删除该图像
if is_watermark:
page.delete_image(xref)
# 保存修改后的PDF
doc.save(output_path)
doc.close()
return True
# 使用示例
if __name__ == "__main__":
input_pdf = "watermarked.pdf"
output_pdf = "watermark_removed.pdf"
# 可选:指定水印颜色范围
# watermark_colors = [((200, 200, 200), (255, 255, 255))] # 浅灰色范围
# remove_watermark_from_pdf(input_pdf, output_pdf, watermark_colors)
# 自动检测
remove_watermark_from_pdf(input_pdf, output_pdf)
print(f"PDF水印去除完成,保存至: {output_pdf}")
```
### 实现原理说明
1. **颜色识别**:通过HSV颜色空间更准确地识别水印颜色范围,比RGB更适合颜色筛选
2. **区域修复**:使用OpenCV的inpaint函数,通过周围像素智能填充水印区域
3. **交互式选择**:提供鼠标点击选择颜色的功能,方便用户定位水印颜色
4. **PDF处理**:PDF去水印通过分析图像对象的颜色特征来判断并删除水印图像
### 使用注意事项
1. 水印去除效果取决于水印与背景的差异,纯色水印效果最佳
2. 复杂背景或半透明水印可能需要调整颜色范围和修复半径
3. PDF去水印可能需要结合文本分析(如检测重复文本模式)提高准确性
4. 商业软件通常有更复杂的算法(如机器学习预测),上述代码为基础实现
这些代码提供了完整的功能框架,你可以根据实际需求调整颜色检测算法和修复参数,以获得更好的去水印效果。 |
|