Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 561f86d

Browse files
Add files via upload
1 parent 60e73e0 commit 561f86d

File tree

8 files changed

+474
-0
lines changed

8 files changed

+474
-0
lines changed

‎src/+helper/adjustBBoxCoordinates.m‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
function boxes = adjustBBoxCoordinates(boxes, imageScale)
2+
% This function gives final coordinates of bounding boxes.
3+
4+
% Copyright 2021 The Mathworks, Inc.
5+
6+
scale = 2 * (1/imageScale);
7+
if isempty(boxes)
8+
boxes = [0 0 0 0 0 0 0 0];
9+
else
10+
num_boxes = size(boxes,1);
11+
for k = 1:num_boxes
12+
if ~isnan(boxes(k,:))
13+
boxes(k,:) = boxes(k,:) * scale;
14+
end
15+
end
16+
end
17+
end

‎src/+helper/connectedComponents.m‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
function [nLabels, labels, stats] = connectedComponents(textScoreComb,connectivity)
2+
% This function computes the connected components labeled image and
3+
% statistics output for each label.
4+
5+
% Copyright 2021 The MathWorks, Inc.
6+
7+
% compute connected components
8+
CC = bwconncomp(textScoreComb,connectivity);
9+
10+
% number of connected components in image
11+
nLabels = CC.NumObjects;
12+
13+
% compute label matrix to label connected components in the image
14+
labels = labelmatrix(CC);
15+
16+
% compute a set of properties like area, boundingbox for each connected component
17+
stats = regionprops(CC,'Area','BoundingBox','Centroid');
18+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
function model = downloadPretrainedCRAFT()
2+
% The downloadPretrainedEfficientDetD0 function downloads the pretrained
3+
% CRAFT network.
4+
%
5+
% Copyright 2021 The MathWorks, Inc.
6+
7+
dataPath = 'model';
8+
modelName = 'craftModel';
9+
netFileFullPath = fullfile(dataPath, modelName);
10+
11+
% Add '.zip' extension to the data.
12+
netFileFull = [netFileFullPath,'.zip'];
13+
14+
if ~exist(netFileFull,'file')
15+
fprintf(['Downloading pretrained', modelName ,'network.\n']);
16+
fprintf('This can take several minutes to download...\n');
17+
url = 'https://ssd.mathworks.com/supportfiles/vision/deeplearning/models/TextDetection/craftModel.zip';
18+
websave (netFileFullPath,url);
19+
unzip(netFileFullPath, dataPath);
20+
model = load([dataPath, '/craftNet.mat']);
21+
else
22+
fprintf('Pretrained EfficientDet-D0 network already exists.\n\n');
23+
unzip(netFileFullPath, dataPath);
24+
model = load([dataPath, '/craftNet.mat']);
25+
end
26+
end

‎src/+helper/getPolygonBoxes.m‎

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
function polys = getPolygonBoxes(boxes, labels, mapper)
2+
% This function generates polygon shape bounding boxes from quadrilateral
3+
% boxes.
4+
5+
% Copyright 2021 The Mathworks, Inc.
6+
7+
% configurations
8+
numCp = 5;
9+
maxLenRatio = 0.7;
10+
expandRatio = 1.45;
11+
rMax = 2.0;
12+
rStep = 0.2;
13+
14+
polys = [];
15+
for i = 1:size(boxes,1)
16+
box = reshape(boxes(i,:,:),[2 4]);
17+
box = box';
18+
% size filter for small instance
19+
w = int32(norm(box(1,:)-box(2,:))+1);
20+
h = int32(norm(box(2,:)-box(3,:))+1);
21+
if w < 10 || h < 10
22+
polys = [polys; NaN([1 28])];
23+
continue;
24+
end
25+
26+
% warp image
27+
tar = double([[1,1];[w,1];[w,h];[1,h]]);
28+
M = fitgeotrans(box,tar,'projective');
29+
wordLabel = imwarp(labels,M,'nearest','OutputView',imref2d([h w]));
30+
try
31+
Minv = inv(transpose(M.T));
32+
catch
33+
polys = [polys; NaN([1 28])];
34+
continue;
35+
end
36+
37+
% binarization for selected label
38+
cur_label = mapper(i);
39+
wordLabel(wordLabel ~= cur_label) = 0;
40+
wordLabel(wordLabel > 0) = 1;
41+
42+
% Polygon generation
43+
44+
% find top/bottom contours
45+
cp = []; % stores the top and bottom location of the column in word label containing word
46+
maxLen = -1;
47+
for j = 1:w
48+
region = find(wordLabel(:,j)~=0);
49+
if size(region,1) < 2
50+
continue
51+
end
52+
cp = [cp; [j,region(1),region(end)]];
53+
lengths = region(end) - region(1) + 1;
54+
if lengths > maxLen
55+
maxLen = lengths;
56+
end
57+
end
58+
59+
% pass if maxLen is similar to h
60+
if h * maxLenRatio < maxLen
61+
polys = [polys; NaN([1 28])];
62+
continue;
63+
end
64+
65+
% get pivot points with fixed length
66+
totSeg = numCp * 2 + 1;
67+
segW = double(w)/totSeg; % segment_width
68+
pp = repmat([nan,nan],numCp,1); % init pivot points
69+
cpSection = repmat([0,0],totSeg,1); % stores center point of each section
70+
segHeight = zeros([1 numCp]);
71+
segNum = 1;
72+
numSec = 0;
73+
prevH = -1;
74+
for k = 1:length(cp)
75+
x = cp(k,1);
76+
sy = cp(k,2);
77+
ey = cp(k,3);
78+
if (segNum) * segW <= (x-1) && segNum <= totSeg
79+
% average previous segment
80+
if numSec == 0
81+
break;
82+
end
83+
cpSection(segNum,:) = [cpSection(segNum,1)/numSec cpSection(segNum,2)/numSec];
84+
numSec = 0;
85+
% reset variables
86+
segNum = segNum + 1;
87+
prevH = -1;
88+
89+
end
90+
% accumulate center points
91+
cy = (sy + ey) * 0.5;
92+
curH = ey - sy + 1;
93+
94+
cpSection(segNum,:) = [cpSection(segNum,1)+x cpSection(segNum,2)+cy];
95+
numSec = numSec + 1;
96+
if mod(segNum,2) ~= 0
97+
continue; % No polygon area
98+
end
99+
if prevH < curH
100+
101+
pp(int32((segNum)/2),:) = [x cy];
102+
segHeight(int32((segNum)/2)) = curH;
103+
prevH = curH;
104+
end
105+
106+
107+
end
108+
% processing last segment
109+
if numSec ~=0
110+
cpSection(end,:) = [cpSection(end,1)/numSec cpSection(end,2)/numSec];
111+
end
112+
113+
% pass if num of pivots is not sufficient or segment width i
114+
% smaller than character height
115+
if any(any(isnan(pp))) || (segW < max(segHeight)*0.25)
116+
polys = [polys;NaN([1 28])];
117+
continue;
118+
end
119+
120+
%calc median maximum of pivot points
121+
halfCharH = median(segHeight)*(expandRatio/2);
122+
123+
%calculate gradient and apply to make horizontal pivots
124+
newPivotPoint = [];
125+
for k = 1:size(pp,1)
126+
x = pp(k,1);
127+
cy = pp(k,2);
128+
dx = cpSection(k*2+1,1) - cpSection(k*2-1,1);
129+
dy = cpSection(k*2+1,2) - cpSection(k*2-1,2);
130+
if dx == 0
131+
newPivotPoint = [newPivotPoint; [x cy-halfCharH x cy+halfCharH]];
132+
continue;
133+
end
134+
rad = -atan2(dy,dx);
135+
c = halfCharH*cos(rad);
136+
s = halfCharH*sin(rad);
137+
newPivotPoint = [newPivotPoint; [x-s cy-c x+s cy+c]];
138+
end
139+
% get edge points to cover character heatmaps
140+
isSppFound = false;
141+
isEppFound = false;
142+
gradS = (pp(2,2)-pp(1,2))/(pp(2,1)-pp(1,1)) + (pp(3,2)-pp(2,2))/(pp(3,1)-pp(2,1));
143+
gradE = (pp(end-1,2)-pp(end,2))/(pp(end-1,1)-pp(end,1)) + (pp(end-2,2)-pp(end-1,2))/(pp(end-2,1)-pp(end-1,1));
144+
for r = 0.5:rStep:rMax
145+
dx = 2 * halfCharH * r;
146+
if ~isSppFound
147+
lineImg = uint8(zeros(size(wordLabel)));
148+
dy = gradS * dx;
149+
p = newPivotPoint(1,:) - [dx dy dx dy];
150+
lineImg = insertShape(lineImg,'Line',[p(1) p(2) p(3) p(4)]);
151+
if (sum(wordLabel & lineImg,'all') == 0) || r+2 * rStep >= rMax
152+
spp = p;
153+
isSppFound = true;
154+
end
155+
end
156+
if ~isEppFound
157+
lineImg = uint8(zeros(size(wordLabel)));
158+
dy = gradE * dx;
159+
p = newPivotPoint(end,:) + [dx dy dx dy];
160+
lineImg = insertShape(lineImg,'Line',[p(1) p(2) p(3) p(4)]);
161+
if (sum(wordLabel & lineImg,'all') == 0) || r+2 * rStep >= rMax
162+
epp = p;
163+
isEppFound = true;
164+
end
165+
end
166+
if isSppFound && isEppFound
167+
break;
168+
end
169+
end
170+
% pass if boundary of polygon is not found
171+
if ~(isSppFound&&isEppFound)
172+
polys = [polys;NaN([1 28])];
173+
continue;
174+
end
175+
176+
% make final polygon
177+
poly = [];
178+
poly = [poly warpCoord(Minv,[spp(1),spp(2)])];
179+
for l = 1:size(newPivotPoint,1)
180+
p = newPivotPoint(l,:);
181+
poly = [poly warpCoord(Minv,[p(1),p(2)])];
182+
end
183+
poly = [poly warpCoord(Minv,[epp(1),epp(2)])];
184+
poly = [poly warpCoord(Minv,[epp(3),epp(4)])];
185+
for l = length(newPivotPoint):-1:1
186+
p = newPivotPoint(l,:);
187+
poly = [poly warpCoord(Minv,[p(3) p(4)])];
188+
end
189+
poly = [poly warpCoord(Minv,[spp(3) spp(4)])];
190+
191+
% add to final result
192+
polys = [polys;poly];
193+
end
194+
end
195+
196+
function res = warpCoord(Minv,pt)
197+
out = Minv * [pt(1) pt(2) 1]';
198+
res = [out(1)/out(3) out(2)/out(3)];
199+
end
200+

‎src/+helper/getQuadBoxes.m‎

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
function [nLabels, labels, bboxes, mapper] = getQuadBoxes(textmap, linkmap, textThreshold, linkThreshold, lowText)
2+
% This function generates Quadrilateral shape bounding boxes.
3+
4+
% Copyright 2021 The Mathworks, Inc.
5+
6+
bboxes = [];
7+
[imgH, imgW] = size(textmap);
8+
mapper = [];
9+
10+
% labeling method
11+
regionScore = imbinarize(textmap,lowText);
12+
affinityScore = imbinarize(linkmap, linkThreshold);
13+
14+
textScoreComb = regionScore + affinityScore;
15+
textScoreComb(textScoreComb>1) = 1;
16+
textScoreComb(textScoreComb<0) = 0;
17+
18+
[nLabels, labels, stats] = helper.connectedComponents(textScoreComb,4);
19+
20+
for k = 1:nLabels
21+
% size filtering
22+
sizes = stats(k).Area;
23+
if sizes < 10
24+
continue
25+
end
26+
27+
% thresholding
28+
if max(textmap(labels==k)) < textThreshold
29+
continue
30+
end
31+
32+
% make segmentation map
33+
segmap = zeros(size(textmap));
34+
segmap(labels==k) = 255;
35+
segmap(affinityScore==1 & regionScore==0) = 0; % remove link area
36+
x = ceil(stats(k).BoundingBox(1)); y = ceil(stats(k).BoundingBox(2));
37+
w = stats(k).BoundingBox(3); h = stats(k).BoundingBox(4);
38+
niter = fix(sqrt(sizes * min(w, h) / (w * h)) * 2);
39+
sx = x - niter; ex = x + w + niter + 1;
40+
sy = y - niter; ey = y + h+ niter + 1;
41+
% boundary check
42+
if sx < 1
43+
sx = 1;
44+
end
45+
if sy < 1
46+
sy = 1;
47+
end
48+
if ex >= imgW
49+
ex = imgW;
50+
end
51+
if ey >= imgH
52+
ey = imgH;
53+
end
54+
kernel = strel('rectangle',[niter + 1, niter + 1]);
55+
segmap(sy:ey, sx:ex) = imdilate(segmap(sy:ey, sx:ex), kernel);
56+
57+
% make box
58+
[i j] = find(segmap~=0);
59+
indices = [i j];
60+
61+
npContours = transpose(circshift((indices'), 1, 1));
62+
bb = helper.minBoundingBox(npContours');
63+
64+
% align diamond-shape
65+
w = norm(bb(:,1) - bb(:,2));
66+
h = norm(bb(:,2) - bb(:,3));
67+
boxRatio = max(w, h) / (min(w, h) + 1e-5);
68+
if abs(1 - boxRatio) <= 0.1
69+
l = min(npContours(:,1)); r = max(npContours(:,1));
70+
t = min(npContours(:,2)); b = max(npContours(:,2));
71+
bb = [l t; r t;r b; l b];
72+
bb = transpose(bb);
73+
end
74+
75+
% make clock-wise order
76+
[~,startidx] = min(sum(bb,1));
77+
bb = circshift(bb, 4-(startidx-1), 2);
78+
boxes = reshape(bb,1,8);
79+
bboxes = [bboxes; boxes];
80+
mapper = [mapper k]; % list of connected components/labels for valid text areas
81+
82+
end
83+
end

‎src/+helper/minBoundingBox.m‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
function bb = minBoundingBox(X)
2+
% This function compute a rotated rectangle of the minimum area enclosing
3+
% the input 2D point set.
4+
5+
% compute the convex hull (CH is a 2*k matrix subset of X)
6+
k = convhull(X(1,:),X(2,:));
7+
CH = X(:,k);
8+
% compute the angle to test, which are the angle of the CH edges as:
9+
% "one side of the bounding box contains an edge of the convex hull"
10+
E = diff(CH,1,2); % CH edges
11+
T = atan2(E(2,:),E(1,:)); % angle of CH edges (used for rotation)
12+
T = unique(mod(T,pi/2)); % reduced to the unique set of first quadrant angles
13+
% create rotation matrix which contains
14+
% the 2x2 rotation matrices for *all* angles in T
15+
% R is a 2n*2 matrix
16+
R = cos( reshape(repmat(T,2,2),2*length(T),2) ... % duplicate angles in T
17+
+ repmat([0 -pi ; pi 0]/2,length(T),1)); % shift angle to convert sine in cosine
18+
% rotate CH by all angles
19+
RCH = R*CH;
20+
% compute border size [w1;h1;w2;h2;....;wn;hn]
21+
% and area of bounding box for all possible edges
22+
bsize = max(RCH,[],2) - min(RCH,[],2);
23+
area = prod(reshape(bsize,2,length(bsize)/2));
24+
% find minimal area, thus the index of the angle in T
25+
[~,i] = min(area);
26+
% compute the bound (min and max) on the rotated frame
27+
Rf = R(2*i+[-1 0],:); % rotated frame
28+
bound = Rf * CH; % project CH on the rotated frame
29+
bmin = min(bound,[],2);
30+
bmax = max(bound,[],2);
31+
% compute the corner of the bounding box
32+
Rf = Rf';
33+
bb(:,4) = bmax(1)*Rf(:,1) + bmin(2)*Rf(:,2);
34+
bb(:,3) = bmin(1)*Rf(:,1) + bmin(2)*Rf(:,2);
35+
bb(:,2) = bmin(1)*Rf(:,1) + bmax(2)*Rf(:,2);
36+
bb(:,1) = bmax(1)*Rf(:,1) + bmax(2)*Rf(:,2);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /