- 
      
 - 
        
Save endolith/334196bac1cac45a4893 to your computer and use it in GitHub Desktop.  
| """ | |
| Automatically detect rotation and line spacing of an image of text using | |
| Radon transform | |
| If image is rotated by the inverse of the output, the lines will be | |
| horizontal (though they may be upside-down depending on the original image) | |
| It doesn't work with black borders | |
| """ | |
| from skimage.transform import radon | |
| from PIL import Image | |
| from numpy import asarray, mean, array, blackman | |
| import numpy as np | |
| from numpy.fft import rfft | |
| import matplotlib.pyplot as plt | |
| try: | |
| # More accurate peak finding from | |
| # https://gist.github.com/endolith/255291#file-parabolic-py | |
| from parabolic import parabolic | |
| def argmax(x): | |
| return parabolic(x, np.argmax(x))[0] | |
| except ImportError: | |
| from numpy import argmax | |
| def rms_flat(a): | |
| """ | |
| Return the root mean square of all the elements of *a*, flattened out. | |
| """ | |
| return np.sqrt(np.mean(np.abs(a) ** 2)) | |
| filename = 'skew-linedetection.png' | |
| # Load file, converting to grayscale | |
| I = asarray(Image.open(filename).convert('L')) | |
| I = I - mean(I) # Demean; make the brightness extend above and below zero | |
| plt.subplot(2, 2, 1) | |
| plt.imshow(I) | |
| # Do the radon transform and display the result | |
| sinogram = radon(I) | |
| plt.subplot(2, 2, 2) | |
| plt.imshow(sinogram.T, aspect='auto') | |
| plt.gray() | |
| # Find the RMS value of each row and find "busiest" rotation, | |
| # where the transform is lined up perfectly with the alternating dark | |
| # text and white lines | |
| r = array([rms_flat(line) for line in sinogram.transpose()]) | |
| rotation = argmax(r) | |
| print('Rotation: {:.2f} degrees'.format(90 - rotation)) | |
| plt.axhline(rotation, color='r') | |
| # Plot the busy row | |
| row = sinogram[:, rotation] | |
| N = len(row) | |
| plt.subplot(2, 2, 3) | |
| plt.plot(row) | |
| # Take spectrum of busy row and find line spacing | |
| window = blackman(N) | |
| spectrum = rfft(row * window) | |
| plt.plot(row * window) | |
| frequency = argmax(abs(spectrum)) | |
| line_spacing = N / frequency # pixels | |
| print('Line spacing: {:.2f} pixels'.format(line_spacing)) | |
| plt.subplot(2, 2, 4) | |
| plt.plot(abs(spectrum)) | |
| plt.axvline(frequency, color='r') | |
| plt.yscale('log') | |
| plt.show() | 
It's help for me, thank you very much.
https://scantailor.org/ can do this very easily
rms_flat has been deprecated since 3.1.0 of matplotlib. link here
Thanks for notice. I use this routine in several projects but without using matplolib, so I think it won't affect me.
Yep. Please comment on these scipy issues: scipy/scipy#16179 scipy/scipy#16189
what to replace rms_flat code ?
@zoldaten I updated the script. Still works:
@endolith thanks !
1.
i see output on skew-linedetection.png:
Rotation: 5.00 degrees
Line spacing: 13.63 pixels
is it correct ?
and sometimes got this:
/home/pi/.local/lib/python3.9/site-packages/skimage/transform/radon_transform.py:75: UserWarning: Radon transform: image must be zero outside the reconstruction circle
  warn('Radon transform: image must be zero outside the '
Rotation: 3.00 degrees
/home/pi/Desktop/rotation_detection.py:69: RuntimeWarning: divide by zero encountered in long_scalars
  line_spacing = N / frequency  # pixels
Line spacing: inf pixels
i saw remark  It doesn't work with black borders
what does it mean ? do you have an example image ?
@zoldaten That's what I get for the example image, yes:
Rotation: 5.00 degrees
Line spacing: 13.63 pixels
@endolith
if you dont mind i ll speed up a bit your code.
now i have time 0:00:01.228  sec (with skew-linedetection.png). on raspberry pi.
the bigger pic i use the more inference time. on  2MiB pic i have already 11 sec.
i used cprofile and found that sinogram = radon(I) eats all time.
to speed up it we need smaller image.
so. we need to replace:
 I = asarray(Image.open(filename).convert('L'))
with this:
import sys
from PIL.Image import Resampling
I = Image.open(filename).convert('L')
I.thumbnail([sys.maxsize, 480], Resampling.LANCZOS)  #resize image keeping aspect ratio. 480 by example. it may be smaller i think.
now i have on  skew-linedetection.png:
0:00:00.739
i didnt tested how the last code works as i need only rotation degrees. and it returns the result.



Hi endolith,
I have tried with my billing image to skew correction. But it is too slow to processing the image.
And it is take around 230 seconds.
How can I resolve this issue?
Thanks