[C++][opencv]基于opencv实现photoshop算法高反差保留

【测试环境】

vs2019

opencv==4.8.0

【效果演示】

【核心代码实现】

Filter.hpp

#ifndef OPENCV2_PHOTOS_FILTER_HPP_
#define OPENCV2_PHOTOS_FILTER_HPP_

#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"

namespace cv {

class Filter {
public:
	Filter();
	virtual ~Filter();

	/**
	 * Gaussian Blur
	 *
	 * @param src [in]  InputArray
	 * @param dst [out] OutputArray
	 * @param radius [in] Gaussian radius, value range [0.1, 250]
	 *
	 * @return 0 if success, else return error code
	 */
	static int GaussianBlur(InputArray src, OutputArray dst, float radius);

	/**
	 * High Pass
	 *
	 * @param src [in]  InputArray
	 * @param dst [out] OutputArray
	 * @param radius [in] Gaussian radius, value range [0.1, 250]
	 *
	 * @return 0 if success, else return error code
	 */
	static int HighPass(InputArray src, OutputArray dst, float radius);


	static int AddNoise(InputArray src, OutputArray dst);

	/**
	 * Ripple
	 *
	 * @param src [in]  InputArray
	 * @param dst [out] OutputArray
	 * @param center [in] center point of ripple
	 * @param radius [in] radius of ripple
	 * @param phases [in]
	 * @param waveLength [in] wave length
	 * @param amplitude [in] amplitude
	 *
	 * @return 0 if success, else return error code
	 */
	static int Ripple(InputArray src, OutputArray dst, Point center, float radius,
			float phases = 0, int waveLength = 0, int amplitude = 0);

	/**
	 * Pinch
	 *
	 * @param src [in]  InputArray
	 * @param dst [out] OutputArray
	 *
	 * @return 0 if success, else return error code
	 */
	static int Pinch(InputArray src, OutputArray dst, float amount = 0.5, Point center = Point(0,0), float radius = 0.6 );


	/**
	 * Kaleidoscope
	 *
	 * @param src [in]  InputArray
	 * @param dst [out] OutputArray
	 *
	 * @return 0 if success, else return error code
	 */
	static int Kaleidoscope(InputArray src, OutputArray dst);
};

} /* namespace cv */

#endif /* OPENCV2_PHOTOS_FILTER_HPP_ */

 Filter.cpp

#include "Filter.hpp"
#include <math.h>
#include <iostream>

#define CLIP_RANGE(value, min, max)  ( (value) > (max) ? (max) : (((value) < (min)) ? (min) : (value)) )
#define COLOR_RANGE(value)  CLIP_RANGE((value), 0, 255)
#define M_PI 3.14
namespace cv {

Filter::Filter() {

}

Filter::~Filter() {
}

/**
 * Gaussian Blur
 *
 * @param src [in]  InputArray
 * @param dst [out] OutputArray
 * @param radius [in] Gaussian radius, value range [0.1, 250]
 *
 * @return 0 if success, else return error code
 */
int Filter::GaussianBlur(InputArray src, OutputArray dst, float radius)
{
	if ( radius < 0.1 ) return -1;
	if ( radius > 250 ) return -1;
	cv::GaussianBlur(src, dst, Size(0,0), radius, radius);
	return 0;
}

/**
 * High Pass
 *
 * @param src [in]  InputArray
 * @param dst [out] OutputArray
 * @param radius [in] Gaussian radius, value range [0.1, 250]
 *
 * @return 0 if success, else return error code
 */
int Filter::HighPass(InputArray src, OutputArray dst, float radius)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	dst.create(src.size(), src.type());
	Mat output = dst.getMat();

	if ( radius < 0.1 ) return -1;
	if ( radius > 250 ) return -1;
	cv::GaussianBlur(input, output, Size(0, 0), radius, radius);

	const uchar *in;
	uchar *out;
	int width = input.cols;
	int height = input.rows;
	int channels = input.channels();

	for (int y = 0; y < height; ++y) {
		in = input.ptr<uchar>(y);
		out = output.ptr<uchar>(y);
		for (int x = 0; x < width; ++x) {
			for (int i = 0; i < 3; ++i ) {
				*out = COLOR_RANGE((*in - *out) + 127);
				++out; ++in;
			}

			for (int i = 0; i < channels-3; ++i) {
				*out++ = *in++;
			}
		}
	}
	return 0;
}



int Filter::AddNoise(InputArray src, OutputArray dst)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	dst.create(src.size(), src.type());
	Mat output = dst.getMat();

	//output = input.clone();
	cv::randn(output, 0.5, 0.25);
}

#define PRINT_VAR(a) std::cout << #a << "= " << a << std::endl

/**
 * Ripple
 *
 * @param src [in]  InputArray
 * @param dst [out] OutputArray
 * @param center [in] center point of ripple
 * @param radius [in] radius of ripple
 * @param phases [in]
 * @param waveLength [in] wave length
 * @param amplitude [in] amplitude
 *
 * @return 0 if success, else return error code
 */
int Filter::Ripple(InputArray src, OutputArray dst, Point center, float radius, float phases,
		int wave_length, int amplitude_in)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	dst.create(src.size(), src.type());
	Mat output = dst.getMat();

	int width = input.cols;
	int height = input.rows;

	float centreX = center.x * 1.0 / width; //0.25;
	float centreY = center.y * 1.0  / height; //0.5;
	//float radius = 90;

	if (radius==0)
		radius = std::min(width, height) / 2;

	int wavelength = (wave_length == 0) ?  radius / 2 : wave_length; //50;
	int amplitude = (amplitude_in == 0) ? wavelength / 4 : amplitude_in; //20

	if ( phases <= 2) phases = 4;
	float phase = M_PI / phases; // default = M_PI / 4;

	float icentreX=width*centreX;
	float icentreY=height*centreY;
	float radius2=radius*radius;

	float dx,dy,new_x,new_y;
	float p,q,x1,y1; //,x0,y0;
	float distance, distance2;
	float amount;

	for (int y=0; y<height; y++)
	{
		for (int x=0; x<width; x++)
		{

			 dx = x-icentreX;
			 dy = y-icentreY;

			 distance2=dx*dx+dy*dy;

			 if (distance2>radius2)
			 {
				 new_x=x;
				 new_y=y;
			 }
			 else
			 {
				 distance=sqrt(distance2);
				 amount=amplitude * sin(distance / wavelength * 2*M_PI - phase);
				 amount =amount* (radius-distance)/radius;
				 amount=amount*wavelength/(distance+0.0001);

				 new_x =x + dx*amount;
				 new_y =y + dy*amount;
			 }

			if(new_x<0)         new_x=0;
			if(new_x>=width-1)  new_x=width-2;
			if(new_y<0)         new_y=0;
			if(new_y>=height-1) new_y=height-2;

			x1=(int)new_x;
			y1=(int)new_y;

			p=new_x-x1;
			q=new_y-y1;

			for (int k=0; k<3; k++)
			{
				output.at<Vec3b>(y, x)[k]=(1-p)*(1-q)*input.at<Vec3b>(y1, x1)[k]+
										(p)*(1-q)*input.at<Vec3b>(y1,x1+1)[k]+
										(1-p)*(q)*input.at<Vec3b>(y1+1,x1)[k]+
										(p)*(q)*input.at<Vec3b>(y1+1,x1+1)[k];
			}

		}
	}
	return 0;
}

int  Filter::Pinch(InputArray src, OutputArray dst, float amount, Point center, float radiusPercent)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	dst.create(src.size(), src.type());
	Mat output = dst.getMat();


	int width = input.cols;
	int height = input.rows;

	float angle = M_PI / 2;
	float centreX = 0.5;
	float centreY = 0.5;

	float radius = std::min(width, height) * radiusPercent ;
	//float amount = 0.5;
	std::cout << amount << std::endl;

	if (radius==0)
		radius = std::min(width, height) / 2;

	float icentreX = width * centreX;
	float icentreY = height * centreY;

	if (center.x != 0 || center.y != 0) {
		icentreX = center.x;
		icentreY = center.y;
	}

	float radius2 = radius * radius;

	float dx, dy, new_x, new_y;
	float p, q, x1 ,y1;
	float distance;
	float a, d, t, s, c, e;

	for (int y = 0; y < height; ++y)
	{
		for (int x = 0; x < width; ++x)
		{
			 dx = x - icentreX;
			 dy = y - icentreY;

			 distance = dx*dx + dy*dy;

			 if (distance > radius2 || distance == 0)
			 {
				 new_x = x;
				 new_y = y;
			 }
			 else
			 {
				d  =  sqrt( distance / radius2 );
				t  =  pow( sin( M_PI * 0.5 * d ), -amount );

				dx  = dx* t;
				dy  = dy* t;

				e = 1 - d;
				a = angle * e * e;

				s = sin( a );
				c = cos( a );

				new_x = icentreX + c*dx - s*dy;
				new_y = icentreY + s*dx + c*dy;
			 }

			if(new_x<0)         new_x = 0;
			if(new_x>=width-1)  new_x = width - 2;
			if(new_y<0)         new_y = 0;
			if(new_y>=height-1) new_y = height - 2;

			x1=(int)new_x;
			y1=(int)new_y;

			p = new_x - x1;
			q = new_y - y1;

			for (int k = 0; k < 3; k++)
			{
				output.at<Vec3b>(y, x)[k]=(1-p)*(1-q)*input.at<Vec3b>(y1, x1)[k]+
										(p)*(1-q)*input.at<Vec3b>(y1,x1+1)[k]+
										(1-p)*(q)*input.at<Vec3b>(y1+1,x1)[k]+
										(p)*(q)*input.at<Vec3b>(y1+1,x1+1)[k];
			}

		}
	}
	return 0;
}


static float Triangle(float x)
{
    float temp_r = fmod(x, 1.0);

    if (temp_r < 0.5) {
       return 2 * temp_r;
    } else {
       return 2 * (1 - temp_r);
    }
}

int Filter::Kaleidoscope(InputArray src, OutputArray dst)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	dst.create(src.size(), src.type());
	Mat output = dst.getMat();

	//const uchar *in;
	//uchar *out;
	int width = input.cols;
	int height = input.rows;
	//int channels = input.channels();

	float angle = M_PI / 4;
	float angle2 = M_PI / 4;
	float centreX = 0.5;
	float centreY = 0.5;
	float sides = 4;

	float icentreX = width * centreX;
	float icentreY = height * centreY;
	float radius = 150;

	float dx, dy, new_x, new_y;
	float p, q, x1, y1;
	float c, r, theta, temp_theta, radius_c;

	for (int i = 0; i < height; ++i)
	{
		for (int j = 0; j < width; ++j)
		{
			 dx = j - icentreX;
			 dy = i - icentreY;

			 theta = atan2(dy, dx) - angle - angle2;
			 r = sqrt( dy * dy + dx * dx );

			 temp_theta = theta / M_PI * sides * 0.5;
			 theta = Triangle (temp_theta);

			 if (radius)
			 {
				c = cos(theta);
				radius_c = radius/c;
				r=radius_c * Triangle( r / radius_c );
			 }


			theta = theta + angle;

			new_x = r * cos(theta) + icentreX;
			new_y = r * sin(theta) + icentreY;

			if(new_x<0)         new_x = 0;
			if(new_x>=width-1)  new_x = width-2;
			if(new_y<0)         new_y = 0;
			if(new_y>=height-1) new_y = height-2;

//		    if (new_x < 0)     continue;
//		    if (new_x >= width-1)   continue;
//		    if (new_y >= height-1)  continue;
//		    if (new_y < 0)  continue;

			x1 = (int) new_x;
			y1 = (int) new_y;

			p = new_x - x1;
			q = new_y - y1;

			//in = input.ptr<uchar>(y1) + x1 * channels;;
			//out = output.ptr<uchar>(y) + x * channels;
			for (int k = 0; k < 3; ++k)
			{
				output.at<Vec3b>(i, j)[k]=
										COLOR_RANGE(
						                (1-p) * (1-q) * input.at<Vec3b>(y1, x1)[k] +
										(p) * (1-q) * input.at<Vec3b>(y1,x1+1)[k]+
										(1-p) * (q) * input.at<Vec3b>(y1+1,x1)[k]+
										(p) * (q)* input.at<Vec3b>(y1+1,x1+1)[k]
						                 );
			}

		}
	}
	return 0;
}



} /* namespace cv */

【完整演示代码下载】

https://download.csdn.net/download/FL1623863129/88600785

【参考文献】

https://blog.csdn.net/c80486/article/details/52506429

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农张三疯

你的打赏是我写文章最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值