[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky] [Facebook]
[Build Your Own Python Action Arcade!]

[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Improve the image-straightening tool in Python

[This example lets you use vertical lines to straighten images in Python]

If you experimented with the previous example Let the user straighten images with vertical lines in Python, you may have noticed that it only allows you to draw lines from left to right or from top to bottom. If you draw lines from right to left or bottom to top, the program gets confused and flips the image in weird ways. For example, the following picture shows the result when I drew the straightening line from right to left.

[The previous program only works if you draw left to right or top to bottom]
This example fixes that issue. It also changes the straightening line so it draws a red and yellow dashed line instead of a thick white line.

Fixing Angles

The following code shows the change needed to handle lines drawn right to left or bottom to top with the new code highlighted in blue.

def mouse_up(self, event): '''Finish drawing.''' if self.new_line1 is None: return # Get the horizontal and vertical differences between the points. dx = self.end_point[0] - self.start_point[0] dy = self.end_point[1] - self.start_point[1] # Make sure the line was drawn left-to-right or top-to-bottom. if abs(dx) > abs(dy): # The line is mostly horizontal. Make sure it's left-to-right. if dx < 0: dx, dy = -dx, -dy # Reverse the line's direction. else: # The line is mostly vertical. Make sure it's top-to-bottom. if dy < 0: dx, dy = -dx, -dy # Reverse the line's direction. # Calculate the line's angle. -180 < new_angle <= 180. new_angle = math.degrees(math.atan2(dy, dx)) dx = abs(dx) dy = abs(dy) # See whether the new angle is closer to vertical than horizontal. if abs(new_angle) > 45: # Convert it into a horizontal angle. new_angle -= 90 # Subtract this angle from the total angle so far. self.angle += new_angle self.show_image() # Delete the orientation line. self.canvas.delete(self.new_line1) self.canvas.delete(self.new_line2) self.start_point = self.end_point = None self.new_line1 = self.new_line2 = None

The change is actually pretty simple. After the program gets the X and Y difference between the line's start and end points, it compares those values to see if the line is more vertical or horizontal. It then checks the line's direction and switches the roles of the two points if necessary.

For example, suppose abs(dx) > abs(dy). In that case, the line is closer to horizontal than vertical so the code checks whether dx < 0. If dx < 0, then the line was drawn right to left. In that case, the code swaps dx and dy. That swaps the roles of the two points so the code can continue as if the line had been drawn left to right.

The code handles vertical lines drawn bottom to top similarly.

Drawing Dashed Lines

The previous example lets you draw thick white lines on the image. Unfortunately white lines don't show up on all background images. For example, if the image has large areas of white, the line won't be visible there. This version draws a dashed red and yellow line so it's visible over any background.

When the program starts, it uses the following code to set some line-drawing parameters.

# Set the line colors and line width. self.line_color1 = 'red' self.line_color2 = 'yellow' self.new_line1 = None self.new_line2 = None self.line_width = 1 self.line_dash = (4, 4)

Later, when you press the left mouse button, the following code creates the initial lines.

# Draw the initial orientation line. self.new_line1 = self.canvas.create_line( [self.start_point, self.end_point], fill=self.line_color1, width=line_width) self.new_line2 = self.canvas.create_line( [self.start_point, self.end_point], fill=self.line_color2, width=line_width, dash=self.line_dash)

Instead of drawing one thick white line, this code draws two lines. First it draws with color line_color1, which in this example is red. It then draws the same line, this time with color line_color1 (yellow) and using a dash pattern. That makes yellow dashes on top of the red line.

Unfortunately, Windows (which I'm using) only supports a few dash patterns and if you try to use some other pattern it defaults to the one that is closest. Feel free to change the dash pattern to experiment or to get a different result if you're not using Windows. If you want more control over dashed lines, use the techniques described in my post Draw dashed lines on a PIL image in Python.

When you move the mouse with its left button pressed, the program uses the Canvas widget's coords method to update the line's position more or less as before.

def mouse_move(self, event): '''Continue drawing.''' if self.new_line1 is None: return # Update the orientation line. self.end_point = (event.x, event.y) self.canvas.coords(self.new_line1, [self.start_point, self.end_point]) self.canvas.coords(self.new_line2, [self.start_point, self.end_point])

The only difference is that the code now has to update both the red and the yellow lines.

Conclusion

This modification to the original program lets it draw dashed lines and handle lines drawn in any direction. The next and final modification lets you overlay a grid on the image so you can see how close to straight it is.

Download the example to experiment with it and to see additional details.

© 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.