(26.02.15 21:56)moustique schrieb: Ich verwende einen Epson 1541 Scanner.
Und einen Twain 5.0 Driver.
Mit Windows XP und dem Programm Omnipage, scanne ich die Buecher.
Dabei waehle ich ein Schwarz/Weiss Format.
...
Danke für die ausführliche Erläuterung.
Das ist freilich nicht ganz das Grundproblem. Die Frage wäre, wie bekommt man verzerrungsfreie Scans, ohne das Buch zu zerstören.
So etwas würde vermutlich gehen:
http://www.google.de/imgres?imgurl=http%...CEQQrQMwDA
(tschuldigung, lange URL). So etwas Ähnliches hat sich wohl auch die Universität in Addis Abeba gebaut, um die weniger Lehrbücher zu "vervielfältigen", jedenfalls laut einem c't-Sonderheft.
Ist aber auch fast egal, alle alten JLPT-Prüfungen, alle Minna no Nihongos und die drei Grammatikbücher sind sowieso schon zerschnitten, der Engpass ist momentan die Nachbearbeitung. O.K., man kanns einfach so lassen, aber ich würde es gern sauber machen.
Vermutlich ist das Forum auch nicht so ganz der optimale Platz, aber seis drum, manchmal findet man Anregungen an den unerwartetsten orten.
Also, das Skript liest ein manuell zu definierendes tif-File ein (das wäre leicht zu ändern, reine Faulheit bisher). Das muss ein Graustufenbild sein. Kontrast, schwarz- und weiss-Level sind egal. Das Skript erzeugt dann einen rein weissen Hintergrund und eine rein schwarze Schrift und lässt an den Rändern der Schrift einen Graustufenübergang. Das sieht sehr viel besser aus, als eine reine schwarz/weiss-Diskriminierung. Innerhalb einer zulässigen Scanabweichung von +/- 5 Grad wird waagerecht ausgerichtet. Staub wird entfernt, die Definition, was Staub ist, erfolgt manuell (im Beispiel 150 Pixel). Das Ganze ist ziemlich quick and dirty, im Prinzip gehts aber. Mir fehlt bisher eine Idee, wie ich kleine Härchen loswerde.
%Bild einlesen
Bild = imread('48.tif');
%
% im Graustufen umwandeln
% Das Skript arbeitet einstweilen nur mit Graustufenbildern. Für eine
% Farbbearbeitung müsste man vermutlich in Kanäle separieren
%
GBild = rgb2gray(Bild);
%
% Im Graustufenbild sind die "weißen" Bereiche irgendwie hellgrau und
% die "schwarzen" Stellen dunkelgrau. Das Ganze hängt von der
% willkürlichen Einstellung der Sacannerparameter ab.
% Ein Histogramm sollte zwei Spitzen zeigen, eine für die "weißen" und eine
% für die "schwarten" teile des Bildes.
% Sinnvoll wäre es jetzt, die beiden Peaks zu greifen und den Tonwert-
% bereich noch etwas kleiner (beispielsweise 20%) als den Tonwertumfang
% zwischen den beiden Spitzen zu wählen. Das würde viel reines Schwarz
% und viel reines Weiß mit einem brauchbaren "Antialiasing" an den
% Kanten geben.
% imhist(Bild);
%
% Der Algorithmus ist zunächst unklar, deshalb werden die Grenzen
% zur Tonwertanpassung zunächst willkürlich gesetzt.
%
% neuBild=imadjust(GBild,[0.3 0.7],[]);
limits = stretchlim(GBild);
diff = limits(2) - limits(1);
tol = diff * 0.2;
limits(1) = limits (1) + tol;
limits (2) = limits(2) - tol;
neuBild=imadjust(GBild,limits,[]);
% figure, imshow(neuBild);
%
% Dieses tonwertkorrigierte Grauwertbild ist die Basis des fertigen
% Dokuments.In den weißen bereichen sind noch Störungen enthalten, die im
% Folgenden einfach nach ihrer Größe aussortiert werden.
%
% Umwandlung in reines Schwarz-Weiss. Das Niveau wird so eingestellt,
% dass alle Abschnitte, die auch nur leicht grau waren, jetzt
% schwarz erscheinen. Dadurch werden alle Staubpartikel in den weißen
% bereichen erfaßt.
%
BW = im2bw(neuBild,0.99);
% figure, imshow(BW);
%
% Der folgende Algorithmus erkennt nur wieße Bereiche, entfernt werden
% sollen allerdings dunkle Staubkörner. Deshlb muss das Bild invertiert
% werden.
%
invBW = ~BW;
% figure, imshow(invBW);
CC = bwconncomp(invBW);
%
% Es wird eine Liste zusammenhängender Objekte erstellt. Die Variable
% CC.NumObjects enthält die Anzahl zusammenhängender Objekte.
% disp(CC.NumObjects);
%
numPixels = cellfun(@numel,CC.PixelIdxList);
%
% Nach dieser Operation ist numPixels ein Vektor, der für jedes der
% CC.NumObjects Elemente deren Größe in Pixeln enthält
%
% disp(numPixels(723));
%
%[smallest,idx] = min(numPixels);
% invBW(CC.PixelIdxList{idx}) = 0;
for k=1:CC.NumObjects
if numPixels(k)<150 % der wert 150 ist willkürlich
[smallest,idx] = min(numPixels);
idx=k;
invBW(CC.PixelIdxList{idx}) = 0;
%
% Wozu dieser Dreizeiler gut isz, weiß ich auch noch nicht so
% genau. invBW(CC.PixelIdxList(k)) = 0; allen tuts jedenfalls
% nicht.
%
disp (numPixels(k));
end
end
[dst,theta]=imdeskew(~invBW,5,0.02);
disp(theta);
% figure, imshow(invBW);
%
% Das tonwertkorrigierte Grauwertbild wird mit dem vom Staub befreiten
% Bitmap multipliziert. Alle im Graustufenbild zu erhaltenden Bereiche sind
% im Bitmap weiss (1) alles im Graustufenbild zu Löschende ist schwarz (0).
%
% Let I be the m-by-n image, and let M be the m-by-n mask.
% The new image, Inew, will be m-by-n and have zeros everywhere there is
% a zero in M and will be the grayscale value wherever M has a one.
% Inew = I.*M;
% If the image is RGB (i.e. I is m-by-n-by-3), then use the command
% Inew = I.*repmat(M,[1,1,3]);
%
% Die direkte Anwendung dieses Schrittes würde das gesamte Bild schwärzen.
% Entweder ist das Bitmap schwarz (die an sich weißen Flächen) oderaber das
% Graustufenbild.
% Vor der Multiplikation wird das Graustufenbild invertiert, das Ergennis
% der Multiplikation muss dann natürlich nochmals nvertiert werden.
%
entstaubtBild = imcomplement(imcomplement(neuBild).*uint8(invBW));
figure, imshow(entstaubtBild);
S=imrotate(entstaubtBild,theta);
figure, imshow(S);
imwrite(S,'48-final.tif','tif');
und dazu imdeskew.m (von einem anderen Autor)
function [ dst, theta ] = imdeskew( src, max_angle, resolution, plotOn )
% FUNCTION: imdeskew is used to deskew an input binary image
% -------------------------------------------------------------------------
% Input:
% src = an input document image, ideally a binary image, but can be
% grayscale or color
% max_angle = max allowed angle to deskew ( default 15 )
% resolution= angle resolution in searching ( default .5 )
% plotON = optional result display
%
% Output:
% dst = optimal deskew image
% theta = optimal deskew angle in degrees
% -------------------------------------------------------------------------
% Sample Code:
% src = imread( '300.tif' );
% [ dst, theta ] = imdeskew( src );
% -------------------------------------------------------------------------
% Image Credits:
% All test images are from 2013 ICDAR handwriting segmentation contest
%
http://users.iit.demokritos.gr/~nstam/IC...urces.html
% -------------------------------------------------------------------------
% Author Info:
% Dr. Yue Wu, ywu03@ece.tufts.edu
% -------------------------------------------------------------------------
% 0. parameter settings
if nargin <= 1
max_angle = 15;
resolution = .5;
plotOn = 1;
elseif nargin <= 2
resolution = .5;
plotOn = 1;
elseif nargin <= 3
plotOn = 1;
else
error('unsupported input format')
end
% input settings
if size( src, 3 ) > 1
display('warning: automatic converting color image to binary')
gray = rgb2gray( src );
src = gray > graythresh( gray ) * 255;
else
if ~islogical( src )
display('warning: automatic converting grayscale image to binary')
src = src > graythresh( src ) * 255;
end
end
% 1. extract black text pixels for analysis
[ h, w ] = size( src );
[ text_x, text_y ] = ind2sub( [ h,w ], find( src(
== 0 ) );
% 2. compute the information entroy of a projection profile
angles = -max_angle : resolution : max_angle;
cx = h/2;
cy = w/2;
len = size( text_x, 1 );
hist_to_prob = @( h ) ( h( h ~= 0 ) / len );
score = [];
for a = angles
sin_a = sin( a / 180 * pi );
cos_a = cos( a / 180 * pi );
sx = round( ( text_x - cx ) * cos_a + ( text_y -cy ) * sin_a + cx );
freq = hist( sx, unique(sx) );
prob = hist_to_prob( freq );
entropy = -prob .* log( prob );
score(end+1) = sum( entropy(
);
end
% 3. generate output
[ val, min_idx ] = min( score );
theta = -angles( min_idx );
dst = not( imrotate( not( src ), theta, 'loose' ) );
if plotOn
figure,subplot(131),plot( -angles, score ), xlabel( 'deskew angle: degree' ), ylabel( 'projection profile entropy ' );
hold on, line( [ -angles( min_idx ), -angles( min_idx )]' , [ min( score )-.1, max( score ) ]', 'color',[1,0,0] )
hold on, text( -angles( min_idx )-1, max( score )-.2 , 'optimal deskew angle' ),axis square
subplot(132),imshow( imerode(src, ones(3) ) ); title( 'before deskew ');
subplot(133),imshow( imerode(dst, ones(3) ) ); title( 'after deskew' );
end
% 4. discussions
% a). Code in Sec 2 is equivalent to doing hough transform, but my
% alternative is more efficient.
% b). It is also possible to find optimal angle in a smarter way, e.g.
% gradient descent;
% c). One may use other criterion instead of information entropy, but
% the entropy metric is already a good one, because it simply means
% that we are looking for an angle making src image most determined.