仿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 pointvector<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;   //RGBCurve RedChannel;   //RedCurve GreenChannel; //GreenCurve BlueChannel;  //BlueCurve* 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) <<  endlnamespace 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_stepline_step = ::abs(line_step);if (line_step == 0) line_step = 1;//validate blank_stepblank_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_ydouble 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 p2if (::abs(p1.x - p2.x) > ::abs(p1.y - p2.y)) {//step in direction of x-coordinationwhile (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);//stepx1 = x2 + step_x * dot_ratio;y1 = y2 + step_y * dot_ratio;}}else {//step in direction of y-coordinationwhile (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);//stepx1 = 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 outputif (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 outputif (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 outputfor (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 lineint n = 0;n = points.size();  //count of pointsdouble* x = (double*)malloc(n * sizeof(int));double* y = (double*)malloc(n * sizeof(int));//create array of x-coordinate and y-coordinate of control pointsvector<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 invalidif (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 pointsint 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-coordinatefor (int i = 0; i < m; ++i) {t[i] = i;}//perform spline, output y-coordinate is stored in array zspline(x, y, n, t, m, z);//create outputfor (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 backgroundmat.setTo(back_color);vector<Point>::iterator it;for (it = points.begin(); it != points.end(); ++it) {cout << "point:  " << it->x << ", " << it->y << endl;}//draw linesdot_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 curvedouble 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 pointsvector<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 pointif (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 pointif (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 pointif (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 pointif (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;elsereturn 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;}//==========================================================// CurvesCurves::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 tablescreateColorTables(colorTables);//adjust each pixelif (hasMask) {
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endiffor (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
#endiffor (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 filesrc = imread("center.jpg");if (!src.data) {cout << "error read image" << endl;return -1;}//create windownamedWindow(window_name, WINDOW_NORMAL);resizeWindow(window_name, 800, 600);imshow(window_name, src);//create Mat for curvescurves_mat = Mat::ones(256, 256, CV_8UC3);//create window for curvesnamedWindow(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()方法逐个添加控制点即可。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出 langchain 1. Prompt 与 Model

示例 从代码入手来看原理 from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI prompt ChatPromptTemplate.from_template("tell me a short joke about…

让python的报错代码只显示第一层

在 Python 中&#xff0c;sys.tracebacklimit 是 sys 模块中的一个属性&#xff0c;它用于控制在错误发生时&#xff0c;Python 解释器显示的堆栈追踪&#xff08;traceback&#xff09;的深度。 具体来说&#xff1a; • 默认行为&#xff1a;当出现未处理的异常时&#xff…

【神经网络】深入理解多层神经网络(深度神经网络

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&#xff01; 深入理解多层神经网络&#x…

气流流型烟雾模型研究相关法规要求及拍摄注意事项

气流模式可视化提供制药设施中实际气流模型的视觉记录。它是目前最广泛接受的、证明关键工艺区域的气流模型满足监管期望的方法。此外&#xff0c;气流模型可视化允许多个职能组织发现气流设计和功能的有效性和意义&#xff0c;特别是在关键领域。 与气流模型相关的法规指南要求…

算法课程笔记——蓝桥云课第25次云课

算法课程笔记——蓝桥云课第25次云课

DDD学习笔记二

模型的要素——用例、视图和构造块 模型的构建步骤 1&#xff09;从用例场景开始&#xff0c;给模型输入概念、属性、术语。 2&#xff09;构建静态领域模型&#xff08;类图&#xff09;&#xff0c;发现领域概念和对象属性。 3&#xff09;构建动态领域模型&#xff08;时序图…

在数字化转型中,数字孪生技术的作用和价值几何?

引言&#xff1a;随着全球化和市场竞争的加剧&#xff0c;企业需要通过数字化转型来提高生产效率、优化产品质量、降低成本&#xff0c;以增强自身竞争力。企业需要通过数字化转型更好地理解客户需求&#xff0c;提供个性化、定制化的产品和服务&#xff0c;从而满足客户的多样…

无人门店社区拼团小程序系统源码

​打造便捷购物新体验 &#x1f6d2; 引言&#xff1a;社区购物新趋势 随着科技的飞速发展&#xff0c;无人门店和社区拼团已经成为购物的新趋势。而结合这两者的“无人门店社区拼团微信小程序”更是为我们带来了前所未有的便捷购物体验。无需排队、无需现金交易&#xff0c;只…

平面点云格网过程及可视化介绍(python)

1、背景介绍 实际人工构造物中&#xff0c;很多物体表面为平面结构&#xff0c;因此将点云投影在二维平面上进行处理&#xff0c;如进行点云面积计算、点云边缘提取等。 具体案例可以参考博客&#xff1a;详解基于格网法统计平面点云面积_点云格网法计算xy投影面积-CSDN博客、点…

Keka for Mac:轻量级压缩解压神器

Keka for Mac是一款专为Mac用户打造的轻量级压缩解压软件&#xff0c;凭借其强大的功能和简洁易用的界面&#xff0c;赢得了众多用户的喜爱。无论是日常办公还是学习娱乐&#xff0c;Keka都能为您提供高效、安全的文件压缩和解压体验。 Keka for Mac v1.4.2中文版下载 产品特点…

Promise入门详解

文章目录 Promise 的介绍和优点&#xff08;为什么需要 Promise&#xff1f;&#xff09;Promise 的基本使用Promise 的状态和回调函数Promise 对象的 3 种状态 Promise 的回调函数Promise的状态图&#xff1a; new Promise() 是同步代码Promise 封装定时器Promise 封装 Ajax 请…

同步时钟系统为何能成为机场时间管理的好伙伴?

在机场这个分秒必争的环境中&#xff0c;精准的时间管理至关重要。同步时钟系统的出现&#xff0c;成为了机场时间管理的得力助手&#xff0c;为机场的高效运行和服务质量的提升发挥了关键作用。 一、同步时钟系统简介 同步时钟系统是一种通过网络技术实现时间同步的高精度计时…

给前端小白的11个建议(少走弯路)

作为一个编程4年的的前端工程师&#xff0c;一路走来踩过许多坑。希望我的经验能让你少踩些坑&#xff0c;在编程的路上走的更顺些&#xff01; 1. 禁用var声明 只使用const或let声明变量。并且首选const&#xff0c;当一个变量需要重新赋值时&#xff0c;才使用let。并且在创…

队列与循环队列

目录 1. 前言&#xff1a; 2. 队列 2.1 队列的概念 2.2 队列的实现 2.3 队列的声明 2.4 队列的初始化 2.5 队列的入队 2.6 队列的出队 2.7 队列获取队头元素 2.8 队列获取队尾元素 2.9 队列获取有效数据个数 2.10 队列判断是否为空 2.11 打印队列 2.12 销毁队列 …

大厂面试经验分享,小白如何在面试中脱颖而出

前言 毕业季&#xff0c;对于每一位即将步入社会的学子来说&#xff0c;都是一个充满挑战和机遇的时刻。作为我的一位好朋友也是好学长&#xff0c;他刚刚在一家顶尖科技公司斩获了他梦寐以求的职位。他深知求职路上的艰辛&#xff0c;因此打算把自己的经验分享给大家&#xf…

【GitOps】使用Google工具JIB实现本地无需安装容器推送镜像,加速SpringCloud项目开发

文章目录 一、效果展示二、简介三、安装Jib插件1、区分环境2、安装插件一、效果展示 本地是window系统,无docker环境,没有任何runtime,使用jib工具打包镜像并推送完成,用时20秒 二、简介 Jib 是 Google 开发的一款开源工具,旨在帮助 Java 开发者更高效地将 Java 应用程…

多业态、多品牌企业,如何实现积分通积通兑?(附大会员方案)

2021年&#xff0c;龙湖升级珑珠为全业态通用积分&#xff0c;招商荟深度接入招商蛇口大会员体系建设&#xff1b;2022年&#xff0c;华润置地大会员“万象星”正式上线&#xff1b;2023年&#xff0c;“蒙牛生活家会员中心”全新上线…… 越来越多地产、零售等行业的集团品牌…

【JavaScript】JS对象和JSON

目录 一、创建JS对象 方式一&#xff1a;new Object() 方式二&#xff1a;{属性名:属性值,...,..., 方法名:function(){ } } 二、JSON格式 JSON格式语法&#xff1a; JSON与Java对象互转: 三、JS常见对象 3.1数组对象API 3.2 其它对象API 一、创建JS对象 方式一&#xff1a;new…

初创企业合规管理中的企业合规义务边界问题

在初创企业的迅猛发展过程中&#xff0c;合规管理是确保公司可持续成长和避免潜在风险的关键因素。而在合规管理中&#xff0c;界定企业边界尤为重要&#xff0c;它关系到企业如何合理规划业务范围、管理内部外部关系以及维护企业形象和法律责任的清晰。 一、初创企业面临的合…

早餐店小程序开发

在快节奏的城市生活中&#xff0c;早餐对于许多人来说是一天中最重要的一餐。然而&#xff0c;传统的早餐店在经营过程中常常面临客流量不稳定、服务效率低下等问题。为了解决这些问题&#xff0c;越来越多的早餐店老板开始寻求利用科技手段提升经营效率。早餐店小程序作为一种…