Faire pivoter les mathématiques de l'image (C #)
Question
J'ai une image avec deux points alignés comme suit:
|----------------|
| |
| . |
| |
| . |
| |
|----------------|
J'ai les deux coordonnées X, Y pour les deux points et je dois faire pivoter l'image de X degrés pour qu'elle ressemble à ceci:
|----------------|
| |
| |
| . . |
| |
| |
|----------------|
En gros, ils s’alignent côte à côte. Quel est le calcul pour cela? (Un exemple de code en C # serait encore mieux, mais pas obligatoire)
La solution
Cela dépend du point que vous souhaitez utiliser comme "centre". pour votre rotation. Appelons le point vers le haut et le point gauche A et celui vers la droite et en dessous du point B. Si vous souhaitez faire pivoter le point A de sorte que le point B s’aligne sur lui, le calcul de l’angle de rotation en radians s’effectue comme suit:
double angle = Math.Atan2(pointB.Y - pointA.Y, pointB.X - pointA.X);
Je ne sais pas comment vous gérez votre image. Par conséquent, ce qui suit ne s'applique que si vous utilisez System.Drawing.Graphics:
myImage.TranslateTransform(-pointA.X, -pointA.Y);
myImage.RotateTransform((float) angle, MatrixOrder.Append);
myImage.TranslateTransform(pointA.X, pointA.Y, MatrixOrder.Append);
J'espère que ça aide.
Autres conseils
Pas de code, désolé, mais une stratégie.
Vous devez pouvoir créer l'image de résultat en échantillonnant l'image source. Vous connaissez l'angle de rotation. Vous devez donc maintenant créer une fonction de mappage qui mappe du résultat vers l'original.
Le code balayerait simplement chaque ligne de l'image résultante et mapperait le pixel sur l'image d'origine. Vous pouvez faire un simple;
for (int plotY = 0; plotY < resultHeight; plotY++)
{
for (int plotX = 0; plotX < resultWidth; plotX++)
{
resultImage.PlotPixel(getOriginalPixel(plotX, plotY, angleOfRotation));
}
}
Nous avons donc besoin de la magie "getOriginalPixel" " méthode, et c’est là que les maths entrent en jeu.
Si nous faisons pivoter l'image de 0 degré, alors plotX, plotY ne représente que le X / Y de l'image d'origine. Mais ce n'est pas amusant.
pickX = x * cos(angle) - y * sin(angle)
pickY = y * cos(angle) + x * sin(angle)
Je pense que mappera au pixel source. Vous aurez besoin de vérifier si c'est hors de portée et juste de retourner noir ou quelque chose comme ça:)
Le code ci-dessous fonctionne
Matrix mRotate = new Matrix();
mRotate.Translate(Convert.ToInt32(Width.Value) / -2, Convert.ToInt32(Height.Value) / -2, MatrixOrder.Append);
mRotate.RotateAt(theta, new Point(0, 0), MatrixOrder.Append);
using (GraphicsPath gp = new GraphicsPath())
{ // transform image points by rotation matrix
gp.AddPolygon(new Point[] { new Point(0, 0), new Point(Convert.ToInt32(Width.Value), 0), new Point(0, Convert.ToInt32(Height.Value)) });
gp.Transform(mRotate);
PointF[] pts = gp.PathPoints;
// create destination bitmap sized to contain rotated source image
Rectangle bbox = boundingBox(bmpSrc, mRotate);
Bitmap bmpDest = new Bitmap(bbox.Width, bbox.Height);
using (Graphics gDest = Graphics.FromImage(bmpDest))
{ // draw source into dest
Matrix mDest = new Matrix();
mDest.Translate(bmpDest.Width / 2, bmpDest.Height / 2, MatrixOrder.Append);
gDest.Transform = mDest;
gDest.DrawImage(bmpSrc, pts);
gDest.DrawRectangle(Pens.Transparent, bbox);
//drawAxes(gDest, Color.Red, 0, 0, 1, 100, "");
return bmpDest;
}
}
Recherchez d'abord le point central:
Point p = new Point((x1-x2)/2, (y1-y2)/2)
Utilisez ensuite la trigonométrie pour résoudre l’angle. Je vais supposer que nous avons redéfini l’origine sur notre point central; j’ai donc un nouveau x3 et un autre y3 sur l’un des points.
hypotenuse = SqrRt(x3^2 + y3^2)
Nous résolvons l'angle inconnu TH
Sin(TH) = opposite / hypotenuse
Donc, pour résoudre ce problème, il nous faut:
TH = Asin(y3 / hypotenuse)
Tourner par TH
.
Voir Wikipedia pour la référence des fonctions trigonométriques
Pour effectuer une transformation 2D générale, il faut résoudre deux problèmes avec 6 inconnues.
'x = xA + yB + C
'y = xD + yE + D
Étant donné 3 points correspondants, vous aurez 6 connus et le système peut être résolu. Dans ce cas, vous ne disposez que de 4 points, puisque vous ne vous souciez pas du cisaillement, mais vous pouvez imaginer introduire un troisième point à 90 degrés de la ligne formée par les deux autres points. Créer une image pivotée est alors (pseudo-codé) quelque chose comme:
for ( y = 0; y < height; y++ )
for ( x = 0; x < width; x++ )
{
newx = x*A + y*B + C;
newy = x*D + y*D + E;
newimage(x,y ) = oldimage( newx, newy );
}
}
Si les performances sont importantes, les multiplications dans la boucle interne peuvent être optimisées en notant que y * B ne change que dans l'apparence extérieure et que newx, newy est modifié par les constantes A et D dans la boucle interne.
Vous devez consulter les matrices de rotation géométrique: Consultez ce site pour obtenir une explication détaillée
Toutefois, pour obtenir de meilleurs résultats, vous devez passer de la destination à la source, puis utiliser la transformation pour chaque pixel de destination:
m = rotation matrix
for each point p in destination
p' = p.m
get pixel p' from source
set pixle p in destination
Il existe, dans les méthodes du framework .net, toutes ces solutions: System.Drawing.Graphics.RotateTransform
et System.Drawing.Graphics.TranslateTransform
. Vous devrez configurer une translation pour déplacer le point de rotation de l'image vers l'origine, puis appliquer la rotation puis une autre translation pour la ramener à la position d'origine. Vous devrez expérimenter ces fonctions pour déterminer leur comportement - je suis au travail pour le moment et je n'ai pas le temps de vous donner un code qui fonctionne. : - (