Skip to content

Commit 561f86d

Browse files
authored
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
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)