仿Photoshop利用曲线对图像调整亮度与色彩

曲线调整是Photoshop的最常用的重要功能之一。对于一个RGB图像,  可以对R,  G,  B 通道进行独立的曲线调整,即,对三个通道分别使用三条曲线(Curve)。还可以再增加一条曲线对 三个通道进行整体调整。 因此,对一个图像,可以用四条曲线调整。最终的结果,是四条曲线调整后合并产生的结果。

图中,横轴是输入,比左到右分别表示0到255.  纵轴是输出,从下到上分别表示0到255.

具体代码分三个实现:

头文件 Curves.h

/*
 * Adjust Curves
 */

#ifndef OPENCV2_PS_CURVES_HPP_
#define OPENCV2_PS_CURVES_HPP_
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;

namespace cv {

	/**
	 * Class of Curve for one channel
	 */
	class Curve {
	protected:
		Scalar color;
		Scalar back_color;
		int tolerance; //
		bool is_mouse_down;
		vector<Point> points;  //control points 
		vector<Point>::iterator current;  //pointer to current point

		vector<Point>::iterator  find(int x);
		vector<Point>::iterator  find(int x, int y);
		vector<Point>::iterator  add(int x, int y);

	public:
		Curve();
		virtual ~Curve();

		int calcCurve(double* z); //

		void draw(Mat& mat);  //
		void mouseDown(int x, int y); 
		bool mouseMove(int x, int y); 
		void mouseUp(int x, int y); 

		void clearPoints(); 
		int  addPoint(const Point& p); 
		int  deletePoint(const Point& p); 
		int  movePoint(const Point& p, int x, int y); 
	};

	/**
	 * Class of Curves for all channels
	 */
	class Curves {
	protected:
		void createColorTables(uchar colorTables[][256]);
	public:
		Curves();
		virtual ~Curves();

		Curve RGBChannel;   //RGB
		Curve RedChannel;   //Red
		Curve GreenChannel; //Green
		Curve BlueChannel;  //Blue

		Curve* CurrentChannel; 

		void draw(Mat& mat); 
		void mouseDown(int x, int y); 
		bool mouseMove(int x, int y);
		void mouseUp(int x, int y); 

	
		int adjust(InputArray src, OutputArray dst, InputArray mask = noArray());

	};
#endif/* OPENCV2_PS_CURVES_HPP_ */

	
	void dot_line(Mat& mat, Point& p1, Point& p2, Scalar& color, int step = 8);

} /* namespace cv */

Curves.cpp

/*
 * Adjust Curves
 *
 */

#include "Curves.hpp"

#ifdef HAVE_OPENMP
#include <omp.h>
#endif

#define SWAP(a, b, t)  do { t = a; a = b; b = t; } while(0)
#define CLIP_RANGE(value, min, max)  ( (value) > (max) ? (max) : (((value) < (min)) ? (min) : (value)) )
#define COLOR_RANGE(value)  CLIP_RANGE((value), 0, 255)

#include <iostream>
#define DEBUG_PRINT(a)  cout << (a) << endl
#define PRINT_VAR(var)  cout << #var << " = " << (var) <<  endl

namespace cv {

	/**
	 * spline function
	 *
	 * @param x [in]  array of x-coordinate of control points
	 * @param y [in]  array of y-coordinate of control points
	 * @param n [in]  count of control points
	 * @param t [in]  array of x-coordinate of output points
	 * @param m [in]  count of output points
	 * @param z [out]  array of y-coordinate of output points
	 */
	static double spline(double* x, double* y, int n, double* t, int m, double* z)
	{
		double* dy = (double*)malloc(n * sizeof(int));
		memset(dy, 0, sizeof(double) * n);
		dy[0] = -0.5;

		//double* ddy = new double[n];
		double* ddy = (double*)malloc(n * sizeof(int));
		memset(ddy, 0, sizeof(double) * n);

		double h1;
		double* s = (double*)malloc(n * sizeof(int));
		double h0 = x[1] - x[0];

		s[0] = 3.0 * (y[1] - y[0]) / (2.0 * h0) - ddy[0] * h0 / 4.0;
		for (int j = 1; j <= n - 2; ++j)
		{
			h1 = x[j + 1] - x[j];
			double alpha = h0 / (h0 + h1);
			double beta = (1.0 - alpha) * (y[j] - y[j - 1]) / h0;
			beta = 3.0 * (beta + alpha * (y[j + 1] - y[j]) / h1);
			dy[j] = -alpha / (2.0 + (1.0 - alpha) * dy[j - 1]);
			s[j] = (beta - (1.0 - alpha) * s[j - 1]);
			s[j] = s[j] / (2.0 + (1.0 - alpha) * dy[j - 1]);
			h0 = h1;
		}
		dy[n - 1] = (3.0 * (y[n - 1] - y[n - 2]) / h1 + ddy[n - 1] * h1 / 2.0 - s[n - 2]) / (2.0 + dy[n - 2]);

		for (int j = n - 2; j >= 0; --j)
		{
			dy[j] = dy[j] * dy[j + 1] + s[j];
		}

		for (int j = 0; j <= n - 2; ++j)
		{
			s[j] = x[j + 1] - x[j];
		}

		for (int j = 0; j <= n - 2; ++j)
		{
			h1 = s[j] * s[j];
			ddy[j] = 6.0 * (y[j + 1] - y[j]) / h1 - 2.0 * (2.0 * dy[j] + dy[j + 1]) / s[j];
		}

		h1 = s[n - 2] * s[n - 2];
		ddy[n - 1] = 6.0 * (y[n - 2] - y[n - 1]) / h1 + 2.0 * (2.0 * dy[n - 1] + dy[n - 2]) / s[n - 2];
		double g = 0.0;
		for (int i = 0; i <= n - 2; i++)
		{
			h1 = 0.5 * s[i] * (y[i] + y[i + 1]);
			h1 = h1 - s[i] * s[i] * s[i] * (ddy[i] + ddy[i + 1]) / 24.0;
			g = g + h1;
		}

		for (int j = 0; j <= m - 1; j++)
		{
			int i;
			if (t[j] >= x[n - 1]) {
				i = n - 2;
			}
			else {
				i = 0;
				while (t[j] > x[i + 1]) {
					i = i + 1;
				}
			}
			h1 = (x[i + 1] - t[j]) / s[i];
			h0 = h1 * h1;
			z[j] = (3.0 * h0 - 2.0 * h0 * h1) * y[i];
			z[j] = z[j] + s[i] * (h0 - h0 * h1) * dy[i];
			h1 = (t[j] - x[i]) / s[i];
			h0 = h1 * h1;
			z[j] = z[j] + (3.0 * h0 - 2.0 * h0 * h1) * y[i + 1];
			z[j] = z[j] - s[i] * (h0 - h0 * h1) * dy[i + 1];
		}

		return(g);
	}

#define WITHIN(x1, delta, x2) ( (delta) > 0 ) ? ( (x1) <= (x2) ) : ( (x1) >= (x2) )
#define EXCEED(x1, delta, x2) ( (delta) > 0 ) ? ( (x1) >= (x2) ) : ( (x1) <= (x2) )

	void dot_line(Mat& mat, const Point& p1, const Point& p2, const Scalar& color,
		int thickness = 1, int lineType = 8, int line_step = 6, int blank_step = 6);

	void dot_line(Mat& mat, const Point& p1, const Point& p2, const Scalar& color,
		int thickness, int lineType, int line_step, int blank_step)
	{
		if (p1 == p2) return;

		//validate line_step
		line_step = ::abs(line_step);
		if (line_step == 0) line_step = 1;

		//validate blank_step
		blank_step = ::abs(blank_step);
		if (blank_step == 0) blank_step = 1;

		//dot_ratio = blank_step / line_step;
		double dot_ratio = blank_step * 1.0 / line_step;

		//calculat step_x, step_y
		double len, step_x, step_y;
		len = sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
		step_x = (p2.x - p1.x) / len * line_step;
		step_y = (p2.y - p1.y) / len * line_step;

		double x1, y1, x2, y2;
		x1 = p1.x;  y1 = p1.y;  //start from Point p1

		//draw line step by step, until meet Point p2
		if (::abs(p1.x - p2.x) > ::abs(p1.y - p2.y)) {
			//step in direction of x-coordination
			while (WITHIN(x1, step_x, p2.x)) {
				if (EXCEED(x1 + step_x * (1 + dot_ratio), step_x, p2.x)) {
					x2 = p2.x;
					y2 = p2.y;
				}
				else if (EXCEED(x1 + step_x, step_x, p2.x)) {
					x2 = p2.x;
					y2 = p2.y;
				}
				else {
					x2 = x1 + step_x;
					y2 = y1 + step_y;
				}
				line(mat, Point(x1, y1), Point(x2, y2), color, thickness, lineType);
				//step
				x1 = x2 + step_x * dot_ratio;
				y1 = y2 + step_y * dot_ratio;
			}

		}
		else {
			//step in direction of y-coordination
			while (WITHIN(y1, step_y, p2.y)) {
				if (EXCEED(y1 + step_y * (1 + dot_ratio), step_y, p2.y)) {
					x2 = p2.x;
					y2 = p2.y;
				}
				else if (EXCEED(y1 + step_y, step_y, p2.y)) {
					x2 = p2.x;
					y2 = p2.y;
				}
				else {
					x2 = x1 + step_x;
					y2 = y1 + step_y;
				}
				line(mat, Point(x1, y1), Point(x2, y2), color, thickness, lineType);
				//step
				x1 = x2 + step_x * dot_ratio;
				y1 = y2 + step_y * dot_ratio;
			}
		}
	}

	Curve::Curve()
	{
		color = Scalar(0, 0, 0);
		back_color = Scalar(255, 255, 255);
		tolerance = 3;
		is_mouse_down = false;
		points.push_back(Point(0, 0));
		points.push_back(Point(255, 255));
		current = points.end();
	}

	Curve::~Curve()
	{
	}


	vector<Point>::iterator  Curve::find(int x)
	{
		vector<Point>::iterator iter;
		for (iter = points.begin(); iter != points.end(); ++iter) {
			if (::abs(iter->x - x) <= tolerance)
				return iter;
		}
		return points.end();
	}

	vector<Point>::iterator  Curve::find(int x, int y)
	{
		vector<Point>::iterator iter;
		for (iter = points.begin(); iter != points.end(); ++iter) {
			if (::abs(iter->x - x) <= tolerance && ::abs(iter->y - y) <= tolerance)
				return iter;
		}
		return points.end();
	}

	vector<Point>::iterator Curve::add(int x, int y)
	{
		vector<Point>::iterator it = find(x);
		if (it == points.end()) {
			Point p(x, y);
			vector<Point>::iterator iter;
			for (iter = points.begin(); iter != points.end(); ++iter) {

				if (iter == points.begin() && iter->x > p.x) {
					DEBUG_PRINT("points insert at beginning");
					return points.insert(iter, p);
				}

				if (iter->x < x && (iter + 1) != points.end() && (iter + 1)->x > p.x) {
					DEBUG_PRINT("points insert");
					return points.insert(iter + 1, p);
				}
			}
			DEBUG_PRINT("points append");
			return points.insert(points.end(), p);
		}
		else {
			return it;
		}
	}

	int Curve::calcCurve(double* output_y)
	{
		//if count of control points is less than 2, return linear output
		if (points.size() < 2) {
			for (int i = 0; i < 256; ++i)
				output_y[i] = 255 - i;
			return 0;
		}

		//if count of control points is 2, return linear output
		if (points.size() == 2) {
			vector<Point>::iterator point1 = points.begin();
			vector<Point>::iterator point2 = point1 + 1;

			double delta_y = 0;
			if (point2->x != point1->x)
				delta_y = (point2->y - point1->y) * 1.0 / (point2->x - point1->x);

			//create output
			for (int i = 0; i < 256; ++i) {
				if (i < point1->x) {
					output_y[i] = point1->y;
				}
				else if (i >= point1->x && i < point2->x) {
					output_y[i] = COLOR_RANGE(point1->y + delta_y * (i - point1->x));
				}
				else {
					output_y[i] = point2->y;
				}
			}
			return 0;
		}


		//the count of control points is greater than 2,  create spline line
		int n = 0;
		n = points.size();  //count of points
		double* x = (double*)malloc(n * sizeof(int));
		double* y = (double*)malloc(n * sizeof(int));
		//create array of x-coordinate and y-coordinate of control points
		vector<Point>::iterator start_point = points.end();
		vector<Point>::iterator end_point = points.end();
		vector<Point>::iterator iter;
		int k = 0;
		for (iter = points.begin(); iter != points.end(); ++iter, ++k) {
			if (k == 0) start_point = iter;
			x[k] = iter->x - start_point->x;
			y[k] = iter->y;
			end_point = iter;
		}

		//if start_point or end_point is invalid
		if (start_point == points.end() || end_point == points.end() || start_point == end_point) {
			for (int i = 0; i < 256; ++i)
				output_y[i] = 255 - i;
			return 1;
		}

		//create array of x-coordinate of output points
		int m = end_point->x - start_point->x;
		double* t = (double*)malloc(n * sizeof(int));
		double* z = (double*)malloc(n * sizeof(int));
		//initialize array of x-coordinate
		for (int i = 0; i < m; ++i) {
			t[i] = i;
		}

		//perform spline, output y-coordinate is stored in array z
		spline(x, y, n, t, m, z);

		//create output
		for (int i = 0; i < 256; ++i) {
			if (i < start_point->x) {
				output_y[i] = start_point->y;
			}
			else if (i >= start_point->x && i < end_point->x) {
				output_y[i] = CLIP_RANGE(z[i - start_point->x], 0, 255);
			}
			else {
				output_y[i] = end_point->y;
			}
		}

		return 0;
	}

	void Curve::draw(Mat& mat)
	{
		int thinkness = 1;
		int n = 0;
		Point lastPoint;

		//clear background
		mat.setTo(back_color);

		vector<Point>::iterator it;
		for (it = points.begin(); it != points.end(); ++it) {
			cout << "point:  " << it->x << ", " << it->y << endl;
		}

		//draw lines
		dot_line(mat, Point(0, 0), Point(255, 0), Scalar(0, 0, 255), 1, 8, 4, 4);
		dot_line(mat, Point(0, 255), Point(255, 255), Scalar(0, 0, 255), 1, 8, 4, 4);

		dot_line(mat, Point(63, 0), Point(63, 255), color, 1, 8, 4, 4);
		dot_line(mat, Point(127, 0), Point(127, 255), color, 1, 8, 4, 4);
		dot_line(mat, Point(191, 0), Point(191, 255), color, 1, 8, 4, 4);
		dot_line(mat, Point(0, 255 - 63), Point(255, 255 - 63), color, 1, 8, 4, 4);
		dot_line(mat, Point(0, 255 - 127), Point(255, 255 - 127), color, 1, 8, 4, 4);
		dot_line(mat, Point(0, 255 - 191), Point(255, 255 - 191), color, 1, 8, 4, 4);

		//create curve
		double z[256];
		calcCurve(z);
		for (int i = 1; i < 256; ++i) {
			line(mat, Point(i - 1, 255 - z[i - 1]), Point(i, 255 - z[i]), color, 1, 8);
		}

		//draw control points
		vector<Point>::iterator iter, iter_next;
		for (iter = points.begin(); iter != points.end(); ++iter, ++n) {
			thinkness = (iter == current) ? -1 : 1;
			rectangle(mat, Point(iter->x - 2, 255 - iter->y + 2),
				Point(iter->x + 2, 255 - iter->y - 2), color, thinkness, 8);
		}
	}


	void Curve::mouseDown(int x, int y)
	{
		y = 255 - y;
		current = add(x, y);
		is_mouse_down = true;
	}

	bool  Curve::mouseMove(int x, int y)
	{
		y = 255 - y;
		if (is_mouse_down) {
			if (current != points.end()) {
				int prev_x = 0;
				int next_x = 255;

				if (current != points.begin()) {
					int prev_y = (current - 1)->y;
					prev_x = (current - 1)->x;

					//match the previous point
					if (points.size() > 2 && ::abs(x - prev_x) <= tolerance && ::abs(y - prev_y) <= tolerance) {
						current--;
						current = points.erase(current);
						DEBUG_PRINT("erase previous");
						return true;
					}

					//if x less than x of previou point
					if (x <= prev_x) {
						//DEBUG_PRINT("less than prev_x");
						return true;
					}
				}

				if ((current + 1) != points.end()) {
					int next_y = (current + 1)->y;
					next_x = (current + 1)->x;

					//match the next point
					if (points.size() > 2 && ::abs(x - next_x) <= tolerance && ::abs(y - next_y) <= tolerance) {
						current = points.erase(current);
						DEBUG_PRINT("erase next");
						return true;
					}

					//if x great than x of next point
					if (x >= next_x) {
						//DEBUG_PRINT("large than next_x");
						return true;
					}
				}

				current->x = CLIP_RANGE(x, 0, 255);
				current->y = CLIP_RANGE(y, 0, 255);

				return true;
			}
		}
		return false;
	}

	void Curve::mouseUp(int x, int y)
	{
		y = 255 - y;
		is_mouse_down = false;
	}


	void Curve::clearPoints()
	{
		points.clear();
	}

	int  Curve::addPoint(const Point& p)
	{
		vector<Point>::iterator iter = add(p.x, p.y);
		if (iter != points.end())
			return 1;
		else
			return 0;
	}

	int  Curve::deletePoint(const Point& p)
	{
		vector<Point>::iterator iter;
		iter = find(p.x, p.y);
		if (iter != points.end()) {
			if (current == iter)
				current = points.end();
			points.erase(iter);
			return 1;
		}
		return 0;
	}

	int  Curve::movePoint(const Point& p, int x, int y)
	{
		vector<Point>::iterator iter;
		iter = find(p.x, p.y);
		if (iter != points.end()) {
			iter->x = x;
			iter->y = y;
			return 1;
		}
		return 0;
	}


	//==========================================================
	// Curves

	Curves::Curves()
	{
		CurrentChannel = &RGBChannel;
	}

	Curves::~Curves()
	{
	}

	void Curves::draw(Mat& mat)
	{
		if (CurrentChannel)  CurrentChannel->draw(mat);
	}

	void Curves::mouseDown(int x, int y)
	{
		if (CurrentChannel)  CurrentChannel->mouseDown(x, y);
	}

	bool Curves::mouseMove(int x, int y)
	{
		if (CurrentChannel)
			return CurrentChannel->mouseMove(x, y);
		return false;
	}

	void Curves::mouseUp(int x, int y)
	{
		if (CurrentChannel)  CurrentChannel->mouseUp(x, y);
	}

	void Curves::createColorTables(uchar colorTables[][256])
	{
		double z[256];

		BlueChannel.calcCurve(z);
		for (int i = 0; i < 256; ++i) {
			colorTables[0][i] = z[i];
		}

		GreenChannel.calcCurve(z);
		for (int i = 0; i < 256; ++i)
			colorTables[1][i] = z[i];

		RedChannel.calcCurve(z);
		for (int i = 0; i < 256; ++i) {
			colorTables[2][i] = z[i];
		}

		uchar value;
		RGBChannel.calcCurve(z);
		for (int i = 0; i < 256; ++i) {
			for (int c = 0; c < 3; c++) {
				value = colorTables[c][i];
				colorTables[c][i] = z[value];
			}
		}
	}

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

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

		bool hasMask = true;
		Mat msk = mask.getMat();
		if (msk.empty())
			hasMask = false;

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

		uchar colorTables[3][256];

		//create color tables
		createColorTables(colorTables);

		//adjust each pixel

		if (hasMask) {
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
			for (int y = 0; y < height; y++) {
				in = input.ptr<uchar>(y);
				out = output.ptr<uchar>(y);
				pmask = msk.ptr<uchar>(y);
				for (int x = 0; x < width; x++) {
					for (int c = 0; c < 3; c++) {
						*out = (colorTables[c][*in] * pmask[x] / 255.0)
							+ (*in) * (255 - pmask[x]) / 255.0;
						out++; in++;
					}
					for (int c = 0; c < channels - 3; c++) {
						*out++ = *in++;
					}
				}
			}
		}
		else {
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
			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 c = 0; c < 3; c++) {
						*out++ = colorTables[c][*in++];
					}
					for (int c = 0; c < channels - 3; c++) {
						*out++ = *in++;
					}
				}
			}
		}

		return 0;
			}


		} /* namespace cv */

 主函数main.cpp

/*
 * test_Curves
 *
 */

#include <cstdio>
#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "Curves.hpp"

using namespace std;
using namespace cv;

static string window_name = "Photo";
static Mat src;

static string curves_window = "Adjust Curves";
static Mat curves_mat;
static int channel = 0;
Curves  curves;

static void invalidate()
{
	curves.draw(curves_mat);
	imshow(curves_window, curves_mat);

	Mat dst;
	curves.adjust(src, dst);
	imshow(window_name, dst);

	int y, x;
	uchar* p;

	y = 150; x = 50;
	p = dst.ptr<uchar>(y) + x * 3;
	cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  ";

	y = 150; x = 220;
	p = dst.ptr<uchar>(y) + x * 3;
	cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  ";

	y = 150; x = 400;
	p = dst.ptr<uchar>(y) + x * 3;
	cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  " << endl;
}

static void callbackAdjustChannel(int, void*)
{
	switch (channel) {
	case 3:
		curves.CurrentChannel = &curves.BlueChannel;
		break;
	case 2:
		curves.CurrentChannel = &curves.GreenChannel;
		break;
	case 1:
		curves.CurrentChannel = &curves.RedChannel;
		break;
	default:
		curves.CurrentChannel = &curves.RGBChannel;
		break;
	}


	invalidate();
}

static void callbackMouseEvent(int mouseEvent, int x, int y, int flags, void* param)
{
	switch (mouseEvent) {
	case EVENT_LBUTTONDOWN:
		curves.mouseDown(x, y);
		invalidate();
		break;
	case EVENT_MOUSEMOVE:
		if (curves.mouseMove(x, y))
			invalidate();
		break;
	case EVENT_LBUTTONUP:
		curves.mouseUp(x, y);
		invalidate();
		break;
	}
	return;
}


int main()
{
	//read image file
	src = imread("center.jpg");
	if (!src.data) {
		cout << "error read image" << endl;
		return -1;
	}

	//create window
	namedWindow(window_name, WINDOW_NORMAL);
	resizeWindow(window_name, 800, 600);
	imshow(window_name, src);

	//create Mat for curves
	curves_mat = Mat::ones(256, 256, CV_8UC3);

	//create window for curves
	namedWindow(curves_window);
	setMouseCallback(curves_window, callbackMouseEvent, NULL);
	createTrackbar("Channel", curves_window, &channel, 3, callbackAdjustChannel);

	invalidate();


	waitKey();

	return 0;

}



代码是复制之后就能运行的,只要替换一下图像文件名。

1, Curves类中定义了四个Curve对象(即四个通道),分别是RedChannel, GreenChannel, BlueChannel 和 RGBChannel.

2,  Curves类支持用鼠标生成曲线,使用方法见代码。

2, Curves.cpp中的spline()函数是生成曲线数值的,即输入一串控制点,通过插值运算,生成一系列的输出值。  

3, 除了用鼠标生成曲线以外, 也可以用程序代码直接生成曲线:

     先使用Curve类的clearPoints()方法清除所有控制点,再调用addPoint()方法逐个添加控制点即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/746909.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++初学者指南-2.输入和输出---流输入和输出

C初学者指南-2.输入和输出—流输入和输出 文章目录 C初学者指南-2.输入和输出---流输入和输出1.定制输入/输出1.1 示例&#xff1a;点坐标输入/输出1.2 流操作符1.3&#xff08;一部分&#xff09;标准库流类型 2. 工具2.1 用getline读取行 2.2 用ignore进行跳转2.3 格式化操作…

武汉星起航:全球化舞台,中国跨境电商品牌力与竞争力双提升

随着全球化步伐的加快和数字技术的迅猛发展&#xff0c;跨境出口电商模式已经成为中国企业海外拓展的重要战略选择。这一模式不仅为中国的中小型企业提供了进军全球市场的机会&#xff0c;更为它们在全球舞台上展示独特的竞争优势提供了强有力的支撑。武汉星起航将从市场拓宽、…

STL迭代器的基础应用

STL迭代器的应用 迭代器的定义方法&#xff1a; 类型作用定义方式正向迭代器正序遍历STL容器容器类名::iterator 迭代器名常量正向迭代器以只读方式正序遍历STL容器容器类名::const_iterator 迭代器名反向迭代器逆序遍历STL容器容器类名::reverse_iterator 迭代器名常量反向迭…

问界M9累计大定破10万,创中国豪车新纪录

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 6月26日消息&#xff0c;华为常务董事、终端BG董事长、智能汽车解决方案BU董事长余承东今日宣布&#xff0c;问界M9上市6个月&#xff0c;累计大定突破10万辆。 这一成绩&#xff0c;也创造了中国市场…

5款名不见经传的小众软件,简单好用

​ 我们在使用一些流行的软件的时候&#xff0c;往往会忽略一些知名度不高但是功能非常强大的软件&#xff0c;有的是因为小众&#xff0c;有的是因为名不见经传&#xff0c;总之因为不出名&#xff0c;有许多的好用的软件都不为大众所知道。 1.桌面美化——Win10 Widgets ​…

为什么需要对数据质量问题进行根因分析?根因分析该怎么做?

在当今的商业环境中&#xff0c;数据已成为企业决策的核心。然而&#xff0c;数据的价值高度依赖于其质量。低质量的数据不仅会降低分析的准确性&#xff0c;还可能导致错误的决策&#xff0c;从而影响企业的竞争力和市场表现。因此&#xff0c;识别和解决数据质量问题是数据管…

c#关键字 ArgumentOutOfRangeException .? IEnumerable string.Join

c# ArgumentOutOfRangeException ArgumentOutOfRangeException 是 C# 中表示某个参数值超出了方法或属性定义的有效范围时引发的一个异常。这个异常通常在尝试访问数组、集合、字符串等的无效索引&#xff0c;或者当传递给方法或属性的参数不在其有效范围内时发生。 例如&…

浅学JVM

一、基本概念 目录 一、基本概念 二、JVM 运行时内存 1、新生代 1.1 Eden 区 1.2. ServivorFrom 1.3. ServivorTo 1.4 MinorGC 的过程 &#xff08;复制- >清空- >互换&#xff09; 1.4.1&#xff1a;eden 、servicorFrom 复制到ServicorTo&#xff0c;年龄1 …

K8S集群进行分布式负载测试

使用K8S集群执行分布式负载测试 本教程介绍如何使用Kubernetes部署分布式负载测试框架&#xff0c;该框架使用分布式部署的locust 产生压测流量&#xff0c;对一个部署到 K8S集群的 Web 应用执行负载测试&#xff0c;该 Web 应用公开了 REST 格式的端点&#xff0c;以响应传入…

C++用Crow实现一个简单的Web程序,实现动态页面,向页面中输入数据并展示

Crow是一个轻量级、快速的C微框架&#xff0c;用于构建Web应用程序和RESTful API。 将处理前端页面的POST请求以添加数据的逻辑添加到 /submit 路由中&#xff0c;并添加了一个新的路由 / 用于返回包含输入框、按钮和表格的完整页面。当用户向表格添加数据时&#xff0c;JavaS…

创新与责任并重!中国星坤连接器的可持续发展战略!

在当今全球化的商业环境中&#xff0c;企业的社会责任、技术创新和产品质量是企业可持续发展的三大支柱。中国星坤正是这样一家企业&#xff0c;它在电子连接技术领域以其卓越的技术创新、坚定的环保责任和严格的生产品控而著称。本文将深入探讨星坤科技如何通过其FAE技术团队的…

浏览器扩展V3开发系列之 chrome.contextMenus 右键菜单的用法和案例

【作者主页】&#xff1a;小鱼神1024 【擅长领域】&#xff1a;JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 chrome.contextMenus 允许开发者向浏览器的右键菜单添加自定义项。 在使用 chrome.contextMenus 之前…

CMN-700(1)CMN-700概述

本章介绍CMN-700&#xff0c;这是用于AMBA5 CHI互连&#xff0c;且可根据需要定制的网格拓扑结构。 1. 关于CMN‐700 CMN‐700是一种可配置扩展的一致性互连网络&#xff0c;旨在满足高端网络和企业计算应用中使用的一致性网络系统的功率、性能和面积(PPA)要求。支持1-256个处…

ES6深潜指南:解锁JavaScript类与继承的高级技巧,让您的代码更加优雅

前言 随着前端技术的迅猛发展&#xff0c;JavaScript已经成为构建现代Web应用不可或缺的编程语言。ES6&#xff08;ECMAScript 2015&#xff09;引入了许多期待已久的特性&#xff0c;其中类&#xff08;Classes&#xff09;和继承机制的引入&#xff0c;极大地增强了JavaScrip…

gc.log中 CMS-concurrent-abortable-preclean

问题 在gc日志中看到 2024-06-26T16:16:07.5040800: 64690272.666: [CMS-concurrent-abortable-preclean-start]CMS: abort preclean due to time 2024-06-26T16:16:12.5530800: 64690277.716: [CMS-concurrent-abortable-preclean: 1.052/5.049 secs] [Times: user1.33 sys0…

Gradle学习-3 Gradle构建的生命周期

Gradle常用文件目录 Gradle 构建的生命周期&#xff0c;有3个阶段: 初始化阶段配置阶段执行阶段 1、初始化阶段 Gradle 支持构建单个工程个多个子工程&#xff0c;初始化阶段主要负责收集所有参与本次构建的子工程&#xff0c;创建一个项目的层次结构&#xff0c;并未每个…

喜报!极限科技新获得一项国家发明专利授权:“搜索数据库的正排索引处理方法、装置、介质和设备”

近日&#xff0c;极限数据&#xff08;北京&#xff09;科技有限公司&#xff08;简称&#xff1a;极限科技&#xff09;新获得一项国家发明专利授权&#xff0c;专利名为 “搜索数据库的正排索引处理方法、装置、介质和设备”&#xff0c;专利号&#xff1a;ZL 2024 1 0479400…

实用软件下载:BetterZip 5最新安装包及详细安装教程

​我们都知道BetterZip是Mac平台上非常受欢迎的文件“解/压缩”工具之一&#xff0c;并且能够生成被Win支持的压缩包。不同领域的应用证明了这是一款功能强大的 Mac 解压缩软件支持 RAR、ZIP等30 种档案格式&#xff0c;可创建小型应用软件。值得一提的是而且能够在不必解压的情…

【教学类-64-05】20240625彩棒鱼骨图(二)AB排列 6.5*1CM 6选2根 30种

背景需求&#xff1a; 【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种-CSDN博客文章浏览阅读897次&#xff0c;点赞23次&#xff0c;收藏13次。【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种https:…

WordPress强大多功能主题模板The7 v9.16.0

模板介绍 The7可以与WPBakery Page Builder&#xff08;原Visual Composer&#xff09;和Ultimate Addons的完全无缝集成。它也与大多数流行的插件完全兼容&#xff0c;例如WooCommerce&#xff0c;WPML&#xff0c;Yoast SEO&#xff0c;All in One WP Migration&#xff0c;…