// 嵌入信息
- (UIImage *)embedMessage:(NSString *)message inImage:(UIImage *)image {
// 将图片转换为位图数据
CGImageRef imageRef = [image CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger bytesPerPixel = 4; // 每个像素占 4 个字节(RGBA)
NSUInteger bitsPerComponent = 8; // 每个颜色分量占 8 位
NSUInteger bytesPerRow = bytesPerPixel * width; // 每行的字节数
unsigned char *rawData = (unsigned char *)calloc(height * width * bytesPerPixel, sizeof(unsigned char));
CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
// 将消息转换为二进制数据,并在末尾添加终止符 '\0'
NSString *messageWithTerminator = [message stringByAppendingString:@"\0"];
NSData *messageData = [messageWithTerminator dataUsingEncoding:NSUTF8StringEncoding];
const char *messageBytes = [messageData bytes];
NSUInteger messageLength = [messageData length];
// 使用 LSB 嵌入消息
NSUInteger byteIndex = 0;
NSUInteger bitIndex = 0;
for (NSUInteger y = 0; y < height; y++) {
for (NSUInteger x = 0; x < width; x++) {
NSUInteger pixelIndex = (width * y + x) * bytesPerPixel;
for (NSUInteger i = 0; i < 3; i++) { // 修改 RGB 通道的最低有效位
if (byteIndex < messageLength) {
unsigned char bit = (messageBytes[byteIndex] >> bitIndex) & 1;
rawData[pixelIndex + i] = (rawData[pixelIndex + i] & 0xFE) | bit;
bitIndex++;
if (bitIndex == 8) {
byteIndex++;
bitIndex = 0;
}
}
}
}
}
// 生成新的图片
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(newImageRef);
free(rawData);
return newImage;
}
// 提取
- (NSString *)extractMessageFromImage:(UIImage *)image {
// 将图片转换为位图数据
CGImageRef imageRef = [image CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger bytesPerPixel = 4; // 每个像素占 4 个字节(RGBA)
NSUInteger bitsPerComponent = 8; // 每个颜色分量占 8 位
NSUInteger bytesPerRow = bytesPerPixel * width; // 每行的字节数
unsigned char *rawData = (unsigned char *)calloc(height * width * bytesPerPixel, sizeof(unsigned char));
CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
// 从 LSB 中提取消息
NSMutableData *messageData = [NSMutableData data];
unsigned char byte = 0;
NSUInteger bitIndex = 0;
BOOL endOfMessage = NO;
for (NSUInteger y = 0; y < height && !endOfMessage; y++) {
for (NSUInteger x = 0; x < width && !endOfMessage; x++) {
NSUInteger pixelIndex = (width * y + x) * bytesPerPixel;
for (NSUInteger i = 0; i < 3 && !endOfMessage; i++) { // 读取 RGB 通道的最低有效位
unsigned char bit = rawData[pixelIndex + i] & 1;
byte |= (bit << bitIndex);
bitIndex++;
if (bitIndex == 8) {
[messageData appendBytes:&byte length:1];
if (byte == '\0') { // 遇到终止符,结束提取
endOfMessage = YES;
break;
}
byte = 0;
bitIndex = 0;
}
}
}
}
// 转换为字符串
NSString *message = [[NSString alloc] initWithData:messageData encoding:NSUTF8StringEncoding];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
free(rawData);
return message;
}
-(void)saveWithImage:(UIImage *)image{
// 将 UIImage 转换为 PNG 数据
NSData *pngData = UIImagePNGRepresentation(image);
// 保存 PNG 数据到相册
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetCreationRequest creationRequestForAssetFromImage:[UIImage imageWithData:pngData]];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success) {
NSLog(@"保存成功");
} else {
NSLog(@"保存失败: %@", error.localizedDescription);
}
}];
}
iOS-盲水印、数字水印
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 在使用知乎,微博的时候,我们经常可以看到自己上传的图片被加上了文字水印,在实际的应用开发过程中,很多客户端都需要开...
- 前言 在前面博文讲解位平面分解的时候,我们就提到过可以通过位平面分解的方式给图像添加水印。而数值水印是图片版权用到...
- Date: 2019.3.24 前言 数字水印技术一般用于版权认证。在实际使用中,嵌入水印的鲁棒性就显...
- 最近要实现一个手写签名功能,要求是,在一定区域绘制文字签名,签名完成后,添加新的水印,并且将图片仅保留签字区域剪切...
- 散列函数是一种公开的数学函数。散列函数运算的输入信息也可叫作报文。散列函数运算后所得到的结果叫作散列码或者叫作消息...