废话开篇:利用 openCV 简单实现一下八倍镜效果
一、实现效果
场景图片
实现效果
二、步骤分析
1、将 场景图 转为矩阵
2、重写 UIPanGestureRecognizer,解决 UIGestureRecognizerStateBegan 状态不响应问题。
3、获取手势拖动过程中的点,进行半径为 100 圆内的全部像素点。
4、重新绘制,生成指定半径圆内 UIImage 图像,替换 UIImageView 的 image
三、外部调用 WSLPaintFlow 八倍镜类
//场景图片
NSString * bundleImage = [[NSBundle mainBundle] pathForResource:@"sence1" ofType:@"jpeg"];
UIImage * image = [UIImage imageWithContentsOfFile:bundleImage];
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 300, self.view.frame.size.width, self.view.frame.size.width * image.size.height / image.size.width)];
imageView.image = image;
[self.view addSubview:imageView];
self.paintFlow = [[WSLPaintFlow alloc] init];
[self.paintFlow amplificationImageView:imageView];
复制代码
四、重写 UIPanGestureRecognizer
这里重写 WSLPanGestureRecognizer 类,继承自 UIPanGestureRecognizer。
因为 UIPanGestureRecognizer 拖拽手势的状态无法捕获点击开始下 UIGestureRecognizerStateBegan 状态,因此简单重写一下。
1、WSLPanGestureRecognizer.m
#import "WSLPanGestureRecognizer.h"
@implementation WSLPanGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
self.state = UIGestureRecognizerStateBegan;
}
@end
复制代码
五、WSLPaintFlow 类实现
1、WSLPaintFlow.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface WSLPaintFlow : NSObject
//放大动作
- (void)amplificationImageView:(UIImageView *)imageView;
@end
NS_ASSUME_NONNULL_END
复制代码
2、WSLPaintFlow.m
#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h> // Mat 和 UIImage互转
#endif
//命名空间
using namespace cv;
#import "WSLPaintFlow.h"
#import "WSLPanGestureRecognizer.h"
@interface WSLPaintFlow()
{
Mat _originalImage;//场景原图
int _r;//八倍镜观察范围
}
//原图Image
@property (nonatomic,strong) UIImage * originalUIImage;
//原图ImageView
@property (nonatomic,weak) UIImageView * imageView;
@end
@implementation WSLPaintFlow
- (oid)amplificationImageView:(UIImageView *)imageView
{
self.imageView = imageView;
if (self.imageView.image) {
//保存原图Image转换后的矩阵信息
self.originalUIImage = imageView.image;
UIImageToMat(self.originalUIImage, _originalImage);
}
self.imageView.userInteractionEnabled = YES;
self.imageView.backgroundColor = [UIColor blackColor];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
//添加手势
WSLPanGestureRecognizer * pan = [[WSLPanGestureRecognizer alloc] initWithTarget:self action: @selector(panAction:)];
[self.imageView addGestureRecognizer:pan];
}
- (void)panAction:(WSLPanGestureRecognizer *)sender
{
switch (sender.state) {
case UIGestureRecognizerStateBegan:
{
//手势开始点击,进行替换渲染
CGPoint currentPoint = [sender locationInView:sender.view];
self.imageView.image = [self amplificationImageWithPoint:currentPoint];
}
break;
case UIGestureRecognizerStateChanged:
{
//手势移动中,进行替换渲染
CGPoint currentPoint = [sender locationInView:sender.view];
self.imageView.image = [self amplificationImageWithPoint:currentPoint];
}
break;
case UIGestureRecognizerStateEnded:
{
//手势抬起,恢复原图
self.imageView.image = self.originalUIImage;
}
break;
default:
break;
}
}
//放大镜
- (UIImage *)amplificationImageWithPoint:(CGPoint)point
{
UIImage * resultImage;
if (self.originalUIImage) {
//按比例获取手势触点对应的图片像素点
int touchX = _originalImage.cols * (point.x / self.imageView.frame.size.width);
int touchY = _originalImage.rows * (point.y / self.imageView.frame.size.height);
//原图转换为RGB
Mat originalShowImg(cvRound(_originalImage.rows), cvRound(_originalImage.cols), CV_8UC1 );
cvtColor(_originalImage, originalShowImg, COLOR_BGR2RGB);
//创建八倍镜小图矩阵,默认黑色背景
Mat circleImg = Mat(cvRound(_r * 2), cvRound(_r * 2), CV_8UC3,CV_RGB(0,0,0));
//八倍镜小图转换为RGB
Mat originalCircleImg = Mat(cvRound(_r), cvRound(_r), CV_8UC1);
cvtColor(circleImg, originalCircleImg, COLOR_BGR2RGB);
//进行像素采集
int r = _r
int rectMinX = touchX - r;
rectMinX = rectMinX < 0 ? 0 : rectMinX;
int rectMinY = touchY - r;
rectMinY = rectMinY < 0 ? 0 : rectMinY;
int rectMaxX = touchX + r;
rectMaxX = rectMaxX > _originalImage.cols ? _originalImage.cols : rectMaxX;
int rectMaxY = touchY + r;
rectMaxY = rectMaxY > _originalImage.rows ? _originalImage.rows : rectMaxY;
for (int x = rectMinX; x < rectMaxX; x++) {
for (int y = rectMinY; y < rectMaxY; y++) {
int distanceX = fabs(x - touchX);
int distanceY = fabs(y - touchY);
//获取当前点距离圆心直线距离,判断像素是否在规定圆的范围内
int distance = hypot(distanceX, distanceY);
if (distance < r) {
//在圆内
int b = originalShowImg.at<Vec3b>(y,x)[0];
int g = originalShowImg.at<Vec3b>(y,x)[1];
int r = originalShowImg.at<Vec3b>(y,x)[2];
//原图上的像素坐标转换为小圆的像素坐标
int circleImgX = x - rectMinX;
int circleImgY = y - rectMinY;
if (circleImgX > _r - 2 && circleImgX < _r + 2 && circleImgY > _r - 2 && circleImgY < _r + 2) {
//绘制准心
originalCircleImg.at<Vec3b>(circleImgY,circleImgX)[0] = 0;
originalCircleImg.at<Vec3b>(circleImgY,circleImgX)[1] = 0;
originalCircleImg.at<Vec3b>(circleImgY,circleImgX)[2] = 255;
} else {
//绘制原图
originalCircleImg.at<Vec3b>(circleImgY,circleImgX)[0] = b;
originalCircleImg.at<Vec3b>(circleImgY,circleImgX)[1] = g;
originalCircleImg.at<Vec3b>(circleImgY,circleImgX)[2] = r;
}
}
}
}
//转换为RGB
Mat resultShowImg(cvRound(originalCircleImg.rows), cvRound(originalCircleImg.cols), CV_8UC1 );
cvtColor(originalCircleImg, resultShowImg, cv::COLOR_BGR2RGB, 3);
resultImage = MatToUIImage(resultShowImg);
}
return resultImage;
}
@end
复制代码
六、总结与思考
简单的八倍镜效果就完成了,个人总结随笔,大神勿笑[抱拳][抱拳][抱拳]