This code sketch allows you to edit the spline with the mouse, it uses the files for this link (attach to your project: overhauser.cpp overhauser. Hpp and vec3.hpp):
The left mouse button adds / moves a point, the right one deletes.

#include <iostream> #include <vector> #include <stdio.h> #include <functional> #include <algorithm> #include <numeric> #include <cstddef> #include "opencv2/opencv.hpp" #include <iostream> #include <fstream> #include "overhauser.hpp" using namespace std; using namespace cv; Mat result; Mat Img; int current_color=0; vector<cv::Point2f> pts_red; vector<cv::Point2f> pts_green; vector<cv::Point2f> pts_blue; Mat curvesImg; int selectedPt=-1; CRSpline* spline_red = 0; CRSpline* spline_green = 0; CRSpline* spline_blue = 0; unsigned char LUT_RED[256]; unsigned char LUT_GREEN[256]; unsigned char LUT_BLUE[256]; // comparison function: bool mycomp (Point2f p1, Point2f p2) { return p1.x<p2.x; } float dist(Point2f p1,Point2f p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } int findNEarestPt(Point2f pt, float maxDist) { vector<Point2f> current_pts_set; current_color=0; if(pt.x>255 && pt.x<512) { current_color=1; } if(pt.x>=512) { current_color=2; } float ptx=pt.x; switch(current_color) { case 0: current_pts_set=pts_red; break; case 1: current_pts_set=pts_green; pt.x-=255; break; case 2: current_pts_set=pts_blue; pt.x-=511; break; } float minDist=FLT_MAX; int ind=-1; for(int i=0;i<current_pts_set.size();++i) { float d=dist(pt,current_pts_set[i]); if(minDist>d) { ind=i; minDist=d; } } if(minDist>maxDist) { ind=-1; } return ind; } float F(float t,float x, CRSpline* spline) { vec3 rv = spline->GetInterpolatedSplinePoint(t); return x-rv.x; } float solveForX(float x,CRSpline* slpine) { float a=-1.0f,b=1.0,c,e=1e-2; c=(a+b)/2; while( (fabs(ba)>e) && (F(c,x,slpine)!=0) ) { if (F(a,x,slpine)*F(c,x,slpine)<0) { b=c; } else { a=c; } c=(a+b)/2; } return c; } int ind=-1; void mouseHandler(int event, int x, int y, int flags, void* param) { Point2f m; mx=x; my=y; curvesImg=Scalar(0,0,0); switch (event) { case cv::EVENT_RBUTTONDOWN: ind=findNEarestPt(m,5); if (ind==-1) { }else { switch(current_color) { case 0: pts_red.erase(pts_red.begin()+ind); break; case 1: pts_green.erase(pts_green.begin()+ind); break; case 2: pts_blue.erase(pts_blue.begin()+ind); break; } ind=-1; } break; case cv::EVENT_LBUTTONDOWN: ind=findNEarestPt(m,5); if (ind==-1) { switch(current_color) { case 0: pts_red.push_back(m); selectedPt=pts_red.size()-1; break; case 1: pts_green.push_back(Point2f(mx-255.0,my)); selectedPt=pts_green.size()-1; break; case 2: pts_blue.push_back(Point2f(mx-511,my)); selectedPt=pts_blue.size()-1; break; } }else { selectedPt=ind; } break; case cv::EVENT_MOUSEMOVE: if(ind!=-1) { switch(current_color) { case 0: pts_red[selectedPt].x=mx; pts_red[selectedPt].y=my; break; case 1: pts_green[selectedPt].x=mx-255; pts_green[selectedPt].y=my; break; case 2: pts_blue[selectedPt].x=mx-511; pts_blue[selectedPt].y=my; break; } } break; case cv::EVENT_LBUTTONUP: ind=-1; break; } std::sort(pts_red.begin(),pts_red.end(),mycomp); if(pts_red.size()>0) { pts_red[pts_red.size()-1].x=255; pts_red[0].x=0; } std::sort(pts_green.begin(),pts_green.end(),mycomp); if(pts_green.size()>0) { pts_green[pts_green.size()-1].x=255; pts_green[0].x=0; } std::sort(pts_blue.begin(),pts_blue.end(),mycomp); if(pts_blue.size()>0) { pts_blue[pts_blue.size()-1].x=255; pts_blue[0].x=0; } for(int i=0;i<pts_red.size();++i) { circle(curvesImg,pts_red[i],5,Scalar(0,0,255),-1,CV_AA); } for(int i=0;i<pts_green.size();++i) { circle(curvesImg,Point2f(pts_green[i].x+255,pts_green[i].y),5,Scalar(0,255,0),-1,CV_AA); } for(int i=0;i<pts_blue.size();++i) { circle(curvesImg,Point2f(pts_blue[i].x+511,pts_blue[i].y),5,Scalar(255,0,0),-1,CV_AA); } if (spline_red) {delete spline_red;} spline_red = new CRSpline(); if (spline_green) {delete spline_green;} spline_green = new CRSpline(); if (spline_blue) {delete spline_blue;} spline_blue = new CRSpline(); for (int i=0;i<pts_red.size();++i) { vec3 v(pts_red[i].x,pts_red[i].y,0); spline_red->AddSplinePoint(v); } for (int i=0;i<pts_green.size();++i) { vec3 v(pts_green[i].x,pts_green[i].y,0); spline_green->AddSplinePoint(v); } for (int i=0;i<pts_blue.size();++i) { vec3 v(pts_blue[i].x,pts_blue[i].y,0); spline_blue->AddSplinePoint(v); } vec3 rv_last(0,0,0); if(pts_red.size()>2) { for(int i=0;i<256;++i) { float t=solveForX(i,spline_red); vec3 rv = spline_red->GetInterpolatedSplinePoint(t); if(rv.y>255){rv.y=255;} if(rv.y<0){rv.y=0;} unsigned char I=(unsigned char)(rv.y); LUT_RED[i]=255-I; if(i>0) { line(curvesImg,Point(rv.x,rv.y),Point(rv_last.x,rv_last.y),Scalar(0,0,255),1); } rv_last=rv; } } rv_last=vec3(0,0,0); if(pts_green.size()>2) { for(int i=0;i<256;++i) { float t=solveForX(i,spline_green); vec3 rv = spline_green->GetInterpolatedSplinePoint(t); if(rv.y>255){rv.y=255;} if(rv.y<0){rv.y=0;} unsigned char I=(unsigned char)(rv.y); LUT_GREEN[i]=255-I; if(i>0) { line(curvesImg,Point(rv.x+255,rv.y),Point(rv_last.x+255,rv_last.y),Scalar(0,255,0),1); } rv_last=rv; } } rv_last=vec3(0,0,0); if(pts_blue.size()>2) { for(int i=0;i<256;++i) { float t=solveForX(i,spline_blue); vec3 rv = spline_blue->GetInterpolatedSplinePoint(t); if(rv.y>255){rv.y=255;} if(rv.y<0){rv.y=0;} unsigned char I=(unsigned char)(rv.y); LUT_BLUE[i]=255-I; if(i>0) { line(curvesImg,Point(rv.x+511,rv.y),Point(rv_last.x+511,rv_last.y),Scalar(255,0,0),1); } rv_last=rv; } } int cur_col=0; if(mx>255 && mx<512) { cur_col=1; } if(mx>=512) { cur_col=2; } Scalar col; switch(cur_col) { case 0: col=Scalar(0,0,255); break; case 1: col=Scalar(0,255,0); break; case 2: col=Scalar(255,0,0); break; } line(curvesImg,Point(0,my),Point(curvesImg.cols,my),col,1); line(curvesImg,Point(mx,0),Point(mx,curvesImg.rows),col,1); imshow("Correction curves",curvesImg); vector<Mat> ch; cv::split(Img,ch); LUT(ch[0],Mat(256,1,CV_8UC1,LUT_BLUE),ch[0]); LUT(ch[2],Mat(256,1,CV_8UC1,LUT_RED),ch[2]); LUT(ch[1],Mat(256,1,CV_8UC1,LUT_GREEN),ch[1]); cv::merge(ch,result); imshow("Transformed",result); } // --------------------------------- // // --------------------------------- //============================================================================== int main( int argc, char** argv ) { for (int i=0;i<256;++i) { LUT_RED[i]=i; LUT_GREEN[i]=i; LUT_BLUE[i]=i; } namedWindow("Image"); namedWindow("Correction curves"); namedWindow("Transformed"); Img=imread("D:\\ImagesForTest\\lena.jpg",1); imshow("Image",Img); curvesImg=Mat::zeros(256,768,CV_8UC3); setMouseCallback("Correction curves", mouseHandler, NULL); waitKey(0); getchar(); }