ImageProcessing-ElectronicPublications / photoquick

Light-weight image viewer with crop,resize,collage, photogrid and filters
GNU General Public License v3.0
4 stars 0 forks source link

Local Un-tilt? #20

Closed zvezdochiot closed 3 years ago

zvezdochiot commented 4 years ago

Hi @ksharindam .

Maybe use grid: grid.png , for "Local Un-tilt"?

zvezdochiot commented 4 years ago

Hi @ksharindam .

Working scheme: Scheme

ksharindam commented 4 years ago

That is difficult to do. Because this requires mapping many points and creating a matrix and then solving that. And I have very poor knowledge in linear algebra. For avoiding matrix algebra, I used Qt's quadToQuad() function, but in this case It can not be used.

zvezdochiot commented 4 years ago

Hi @ksharindam .

Linear algebra is unnecessary. You need to apply to each quadrilateral (green) Un-tilt into a rectangle (blue), and then "sew" the rectangles.

Initial grid (blue) - uniform, set by parameters M and N. The extreme nodes cannot be touched, only the central ones.

RESTRICTION: A node cannot go past the next node.

See also: https://github.com/ImageProcessing-ElectronicPublications/photoquick-examples/tree/main/main/transform/un-tilt_local

zvezdochiot commented 3 years ago

Hi @ksharindam .

Meshing and manipulating with it is a problem for me. Therefore, I switched to considering Warp (coordinate displacement) in the form of a sum of displacement vectors:

dv{x,y} = v.{x,y}2 - v.{x,y}1
L2v = dvx * dvx + dvy * dvy
d{x,y} = p.{x,y} - v.{x,y}1
L2 = dx * dx + dy * dy
w = 1.0 / (1.0 + L2 / L2v)
dp{x,y} = dv{x,y} * w
dps{x,y} = sum(dp{x,y}, {v}) / sum(w, {v})
dpc{x,y} = sum(dv{x,y}. {v}) / count({v})
p.{x,y} += (dps{x,y} - dpc{x,y})

Any ideas how to organize the offset vectors? sample

zvezdochiot commented 3 years ago

Hi @ksharindam .

I study https://github.com/winddyhe/imagedigitizer how the most suitable one. Maybe there is something closer?

zvezdochiot commented 3 years ago

Hi @ksharindam .

Maybe use mask for horizontal warping?
horizontal_mask Then the two halves of the image are easily transformed by stretching along the columns to the centerline.

See also #30 .

PS: The middle line is determined by either the upper or the lower area: xm = height - area_h / width = area_d /width.

ksharindam commented 3 years ago

I have no idea how to do this. Previously created mask can not be applied after a transformation.

zvezdochiot commented 3 years ago

@ksharindam say:

I have no idea how to do this.

❓ Is it possible in Qt to somehow draw broken upper and lower lines with the condition x[i + 1]> x[i]? I just need this. I know what to do with them.

🔓 Give me an interface without action (empty action), and the transformation is mine.

zvezdochiot commented 3 years ago

Hi @ksharindam .

Dewapring requires:

Dialog:

dialog

Two lines of N nodes:

tool

ksharindam commented 3 years ago

I still have no idea how to do it. Did you find a way to do it?

zvezdochiot commented 3 years ago

Hi @ksharindam .

While I think. My GUI is bad.

zvezdochiot commented 3 years ago

Hi @ksharindam .

Now I'm playing with the patch:

diff -Nard photoquick-4.3.8-orig/src/transform.cpp photoquick-4.3.8-temp/src/transform.cpp
358,361c358,367
<     p1 = topleft = QPoint(0,0);
<     p2 = topright = QPoint(pixmap.width()-1, 0);
<     p3 = btmleft = QPoint(0, pixmap.height()-1);
<     p4 = btmright = QPoint(pixmap.width()-1, pixmap.height()-1);
---
>     int i, sx = 1, sy = 1, st, xt, yt;
>     for (i = 0; i < 4; i++)
>     {
>         xt = (sx < 0) ? (pixmap.width() - 1) : 0;
>         yt = (sy < 0) ? (pixmap.height() - 1) : 0;
>         p[i] = pt[i] = QPoint(xt, yt);
>         st = sx;
>         sx = -sy; //1, -1, -1,  1
>         sy = st;  //1,  1, -1, -1
>     }
383,392c389,398
<     if (QRect(topleft, QSize(60, 60)).contains(clk_pos))
<         clk_area = 1;   // Topleft is clicked
<     else if (QRect(topright, QSize(-60, 60)).contains(clk_pos))
<         clk_area = 2;   // Topright is clicked
<     else if (QRect(btmleft, QSize(60, -60)).contains(clk_pos))
<         clk_area = 3;   // Bottomleft is clicked
<     else if (QRect(btmright, QSize(-60, -60)).contains(clk_pos))
<         clk_area = 4;   // bottom right corner clicked
<     else
<         clk_area = 0;
---
>     clk_area = 0;
>     int i, sx = 1, sy = 1, st;
>     for (i = 0; i < 4; i++)
>     {
>         if (QRect(pt[i], QSize(sx * 60, sy * 60)).contains(clk_pos))
>             clk_area = i + 1;
>         st = sx;
>         sx = -sy; //1, -1, -1,  1
>         sy = st;  //1,  1, -1, -1
>     }
399,402c405,406
<     topleft = p1;
<     topright = p2;
<     btmleft = p3;
<     btmright = p4;
---
>     for (int i = 0; i < 4; i++)
>         pt[i] = p[i];
412,434c416,419
<     switch (clk_area) {
<     case 1 : { // Top left corner is clicked
<         new_pt = topleft + moved;
<         p1 = QPoint(MAX(0, new_pt.x()), MAX(0, new_pt.y()));
<         break;
<     }
<     case 2 : { // Top right corner is clicked
<         new_pt = topright + moved;
<         p2 = QPoint(MIN(last_pt.x(), new_pt.x()), MAX(0, new_pt.y()));
<         break;
<     }
<     case 3 : { // Bottom left corner is clicked
<         new_pt = btmleft + moved;
<         p3 = QPoint(MAX(0, new_pt.x()), MIN(last_pt.y(), new_pt.y()));
<         break;
<     }
<     case 4 : { // Bottom right corner is clicked
<         QPoint new_pt = btmright + moved;
<         p4 = QPoint(MIN(last_pt.x(), new_pt.x()), MIN(last_pt.y(), new_pt.y()));
<         break;
<     }
<     default:
<         break;
---
>     if (clk_area > 0)
>     {
>         new_pt = pt[clk_area - 1] + moved;
>         p[clk_area - 1] = QPoint(MIN(last_pt.x(), MAX(0, new_pt.x())), MIN(last_pt.y(), MAX(0, new_pt.y())));
445c430
<     polygon << p1<< p2<< p4<< p3;
---
>     polygon << p[0] << p[1] << p[2] << p[3];
448,455c433,443
<     calcArc(p1, p2, p3, p4, start, span);
<     painter.drawArc(p1.x()-30, p1.y()-30, 60,60, 16*start, 16*span);
<     calcArc(p2, p1, p4, p3, start, span);
<     painter.drawArc(p2.x()-30, p2.y()-30, 60,60, 16*start, 16*span);
<     calcArc(p3, p4, p1, p2, start, span);
<     painter.drawArc(p3.x()-30, p3.y()-30, 60,60, 16*start, 16*span);
<     calcArc(p4, p2, p3, p1, start, span);
<     painter.drawArc(p4.x()-30, p4.y()-30, 60,60, 16*start, 16*span);
---
>     int i, j0, j1, j2, j3;
>     for (i = 0; i < 4; i++)
>     {
>         j0 = i;
>         j3 = (i + 2) % 4;
>         j1 = ((i - 1) < 0) ? (1 - i) : (i - 1);
>         j2 = (j1 + 2) % 4;
> 
>         calcArc(p[j0], p[j1], p[j2], p[j3], start, span);
>         painter.drawArc(p[i].x() - 30, p[i].y() - 30, 60, 60, 16 * start, 16 * span);
>     }
458c446
<     polygon<< p1+QPoint(1,1)<< p2+QPoint(-1,1)<< p4+QPoint(-1,-1)<< p3+QPoint(1,-1);
---
>     polygon << (p[0] + QPoint(1,1)) << (p[1] + QPoint(-1,1)) << (p[2] + QPoint(-1,-1)) << (p[3] + QPoint(1,-1));
476,479c464,465
<     p1 = QPoint(p1.x()/scaleX, p1.y()/scaleY);
<     p2 = QPoint(p2.x()/scaleX, p2.y()/scaleY);
<     p3 = QPoint(p3.x()/scaleX, p3.y()/scaleY);
<     p4 = QPoint(p4.x()/scaleX, p4.y()/scaleY);
---
>     for (int i = 0; i < 4; i++)
>         p[i] = QPoint(p[i].x() / scaleX, p[i].y() / scaleY);
482,483c468,469
<         mxy = meanx2(p1, p2, p3, p4);
<         sxy = stdevx2(p1, p2, p3, p4);
---
>         mxy = meanx2(p[0], p[1], p[2], p[3]);
>         sxy = stdevx2(p[0], p[1], p[2], p[3]);
493,494c479,480
<         max_w = MAX(p2.x()-p1.x(), p4.x()-p3.x());
<         max_h = MAX(p3.y()-p1.y(), p4.y()-p2.y());
---
>         max_w = MAX(p[1].x() - p[0].x(), p[2].x() - p[3].x());
>         max_h = MAX(p[3].y() - p[0].y(), p[2].y() - p[1].y());
497c483
<     mapFrom << p1<< p2<< p3<< p4;
---
>     mapFrom << p[0] << p[1] << p[2] << p[3];
499c485
<     mapTo << QPointF(min_w,min_h)<< QPointF(max_w,min_h)<< QPointF(min_w,max_h)<< QPointF(max_w,max_h);
---
>     mapTo << QPointF(min_w, min_h) << QPointF(max_w, min_h) << QPointF(max_w, max_h) << QPointF(min_w, max_h);
510,512c496,498
<         topleft = trueMtx.map(p1);
<         btmright = trueMtx.map(p4);
<         canvas->data->image = img.copy(QRect(topleft, btmright));
---
>         pt[0] = trueMtx.map(p[0]);
>         pt[2] = trueMtx.map(p[2]);
>         canvas->data->image = img.copy(QRect(pt[0], pt[2]));
diff -Nard photoquick-4.3.8-orig/src/transform.h photoquick-4.3.8-temp/src/transform.h
74c74
<     QPoint topleft, topright, btmleft, btmright, clk_pos, p1,p2,p3,p4;
---
>     QPoint pt[4], clk_pos, p[4];

But I'm pretty sure there is a class in qt that creates a QPoint list instead of an array. What class is it and what is it with? If I can figure it out, I'll fix the PerspectiveTransform and do Dewarping based on it.

ksharindam commented 3 years ago

There is no specific class for list of QPoints. But you can use QList or QVector. Like... QList pts;

On Tue, 18 May, 2021, 8:32 PM звездочёт, @.***> wrote:

Hi @ksharindam https://github.com/ksharindam .

Now I'm playing with the patch:

diff -Nard photoquick-4.3.8-orig/src/transform.cpp photoquick-4.3.8-temp/src/transform.cpp358,361c358,367< p1 = topleft = QPoint(0,0);< p2 = topright = QPoint(pixmap.width()-1, 0);< p3 = btmleft = QPoint(0, pixmap.height()-1);< p4 = btmright = QPoint(pixmap.width()-1, pixmap.height()-1);---> int i, sx = 1, sy = 1, st, xt, yt;> for (i = 0; i < 4; i++)> {> xt = (sx < 0) ? (pixmap.width() - 1) : 0;> yt = (sy < 0) ? (pixmap.height() - 1) : 0;> p[i] = pt[i] = QPoint(xt, yt);> st = sx;> sx = -sy; //1, -1, -1, 1> sy = st; //1, 1, -1, -1> }383,392c389,398< if (QRect(topleft, QSize(60, 60)).contains(clk_pos))< clk_area = 1; // Topleft is clicked< else if (QRect(topright, QSize(-60, 60)).contains(clk_pos))< clk_area = 2; // Topright is clicked< else if (QRect(btmleft, QSize(60, -60)).contains(clk_pos))< clk_area = 3; // Bottomleft is clicked< else if (QRect(btmright, QSize(-60, -60)).contains(clk_pos))< clk_area = 4; // bottom right corner clicked< else< clk_area = 0;---> clk_area = 0;> int i, sx = 1, sy = 1, st;> for (i = 0; i < 4; i++)> {> if (QRect(pt[i], QSize(sx 60, sy 60)).contains(clk_pos))> clk_area = i + 1;> st = sx;> sx = -sy; //1, -1, -1, 1> sy = st; //1, 1, -1, -1> }399,402c405,406< topleft = p1;< topright = p2;< btmleft = p3;< btmright = p4;---> for (int i = 0; i < 4; i++)> pt[i] = p[i];412,434c416,419< switch (clk_area) {< case 1 : { // Top left corner is clicked< new_pt = topleft + moved;< p1 = QPoint(MAX(0, new_pt.x()), MAX(0, new_pt.y()));< break;< }< case 2 : { // Top right corner is clicked< new_pt = topright + moved;< p2 = QPoint(MIN(last_pt.x(), new_pt.x()), MAX(0, new_pt.y()));< break;< }< case 3 : { // Bottom left corner is clicked< new_pt = btmleft + moved;< p3 = QPoint(MAX(0, new_pt.x()), MIN(last_pt.y(), new_pt.y()));< break;< }< case 4 : { // Bottom right corner is clicked< QPoint new_pt = btmright + moved;< p4 = QPoint(MIN(last_pt.x(), new_pt.x()), MIN(last_pt.y(), new_pt.y()));< break;< }< default:< break;---> if (clk_area > 0)> {> new_pt = pt[clk_area - 1] + moved;> p[clk_area - 1] = QPoint(MIN(last_pt.x(), MAX(0, new_pt.x())), MIN(last_pt.y(), MAX(0, new_pt.y())));445c430< polygon << p1<< p2<< p4<< p3;---> polygon << p[0] << p[1] << p[2] << p[3];448,455c433,443< calcArc(p1, p2, p3, p4, start, span);< painter.drawArc(p1.x()-30, p1.y()-30, 60,60, 16start, 16span);< calcArc(p2, p1, p4, p3, start, span);< painter.drawArc(p2.x()-30, p2.y()-30, 60,60, 16start, 16span);< calcArc(p3, p4, p1, p2, start, span);< painter.drawArc(p3.x()-30, p3.y()-30, 60,60, 16start, 16span);< calcArc(p4, p2, p3, p1, start, span);< painter.drawArc(p4.x()-30, p4.y()-30, 60,60, 16start, 16span);---> int i, j0, j1, j2, j3;> for (i = 0; i < 4; i++)> {> j0 = i;> j3 = (i + 2) % 4;> j1 = ((i - 1) < 0) ? (1 - i) : (i - 1);> j2 = (j1 + 2) % 4;> > calcArc(p[j0], p[j1], p[j2], p[j3], start, span);> painter.drawArc(p[i].x() - 30, p[i].y() - 30, 60, 60, 16 start, 16 span);> }458c446< polygon<< p1+QPoint(1,1)<< p2+QPoint(-1,1)<< p4+QPoint(-1,-1)<< p3+QPoint(1,-1);---> polygon << (p[0] + QPoint(1,1)) << (p[1] + QPoint(-1,1)) << (p[2] + QPoint(-1,-1)) << (p[3] + QPoint(1,-1));476,479c464,465< p1 = QPoint(p1.x()/scaleX, p1.y()/scaleY);< p2 = QPoint(p2.x()/scaleX, p2.y()/scaleY);< p3 = QPoint(p3.x()/scaleX, p3.y()/scaleY);< p4 = QPoint(p4.x()/scaleX, p4.y()/scaleY);---> for (int i = 0; i < 4; i++)> p[i] = QPoint(p[i].x() / scaleX, p[i].y() / scaleY);482,483c468,469< mxy = meanx2(p1, p2, p3, p4);< sxy = stdevx2(p1, p2, p3, p4);---> mxy = meanx2(p[0], p[1], p[2], p[3]);> sxy = stdevx2(p[0], p[1], p[2], p[3]);493,494c479,480< max_w = MAX(p2.x()-p1.x(), p4.x()-p3.x());< max_h = MAX(p3.y()-p1.y(), p4.y()-p2.y());---> max_w = MAX(p[1].x() - p[0].x(), p[2].x() - p[3].x());> max_h = MAX(p[3].y() - p[0].y(), p[2].y() - p[1].y());497c483< mapFrom << p1<< p2<< p3<< p4;---> mapFrom << p[0] << p[1] << p[2] << p[3];499c485< mapTo << QPointF(min_w,min_h)<< QPointF(max_w,min_h)<< QPointF(min_w,max_h)<< QPointF(max_w,max_h);---> mapTo << QPointF(min_w, min_h) << QPointF(max_w, min_h) << QPointF(max_w, max_h) << QPointF(min_w, max_h);510,512c496,498< topleft = trueMtx.map(p1);< btmright = trueMtx.map(p4);< canvas->data->image = img.copy(QRect(topleft, btmright));---> pt[0] = trueMtx.map(p[0]);> pt[2] = trueMtx.map(p[2]);> canvas->data->image = img.copy(QRect(pt[0], pt[2]));diff -Nard photoquick-4.3.8-orig/src/transform.h photoquick-4.3.8-temp/src/transform.h74c74< QPoint topleft, topright, btmleft, btmright, clk_pos, p1,p2,p3,p4;---> QPoint pt[4], clk_pos, p[4];

But I'm pretty sure there is a class in qt that creates a QPoint list instead of an array. What class is it and what is it with? As I figure it out, If I can figure it out, I'll fix the PerspectiveTransform and do Dewarping based on it.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ImageProcessing-ElectronicPublications/photoquick/issues/20#issuecomment-843247573, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEKWXPVOHMOA2XHPJXKZOLLTOJ6PVANCNFSM4Q7UUWJQ .

zvezdochiot commented 3 years ago

@ksharindam say:

There is no specific class for list of QPoints.

QPolygon?

ksharindam commented 3 years ago

Yes, it is vector of QPoints

On Tue, 18 May, 2021, 10:25 PM звездочёт, @.***> wrote:

@ksharindam https://github.com/ksharindam say:

There is no specific class for list of QPoints.

QPolygon?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ImageProcessing-ElectronicPublications/photoquick/issues/20#issuecomment-843359091, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEKWXPWM7TES7H33HPF6R5DTOKLWJANCNFSM4Q7UUWJQ .