matlab - Find a bounding box which can cover a character in image - algorithm

An image represents a Korean character. All values of the image are composed of 0 or 255.
Then I wanna get a bounding box which can perfectly cover the character in the image.
For example:
Input image
Output image(What I want is to get the vertices of the red box.)
I have an idea to do this but I think that is not good:
Step 1. Find the leftmost and uppermost index in the image, say (l, up)
Step 2. Find the rightmost and lowermost index in the image, say (r, low)
Step 3. Then the square(bounding box) whose one of vertices is (l, up) and (r, low) can cover a character in an image.
Is there a good idea or matlab library for this?

Even without the Matlab image processing toolbox, you could extract the left,right,top,bottom boundary indices of the input image using find.
Assuming the image is a binary matrix (logical 1 or 0) called "input":
leftBoundary = find(input,1,'first');
rightBoundary = find(input,1,'last');
topBoundary = find(input',1,'first');
BotBoundary = find(input',1,'last');
Keep in mind these are linear indices. You can use other summoning methods of find to get normal subscripts if needed
[row,col] = find(___)

You can accomplish this by using the function any to find logical indices for the rows and columns that contain any part of your character, then find to get the row and column indices of the extremities:
img = imread('Y2ZIW.png'); % Load RGB image you posted
bw = ~im2bw(img); % Convert to binary and negate
rowIndex = any(bw, 2); % N-by-1 logical index for rows
colIndex = any(bw, 1); % 1-by-N logical index for columns
boundBox = [find(colIndex, 1, 'first') find(rowIndex, 1, 'first'); ...
find(colIndex, 1, 'last') find(rowIndex, 1, 'last')];
This gives us the following 2-by-2 matrix for boundBox, which we can use as indices into your image to crop just the region containing the character:
boundBox =
71 57 % Left and upper corner index
214 180 % Right and lower corner index
subRegion = bw(boundBox(1, 2):boundBox(2, 2), boundBox(1, 1):boundBox(2, 1));
imshow(subRegion);
And here's the plot of the cropped region:
If you want a minimum one-pixel border around your cropped region, you can modify the calculation for boundBox like so:
boundBox = [find(colIndex, 1, 'first')-1 find(rowIndex, 1, 'first')-1; ...
find(colIndex, 1, 'last')+1 find(rowIndex, 1, 'last')+1];

I1 = imread('Y2ZIW.png') ;
I = rgb2gray(I1) ;
[y,x] = find(I==0) ;
%% Bounding box
x0 = min(x) ; x1 = max(x) ;
y0 = min(y) ; y1 = max(y) ;
B = abs(x1-x0) ;
L = abs(y1-y0) ;
BB = [x0 y0 ; x0 y0+L ; x0+B y0+L ; x0+B y0 ; x0 y0] ;
imshow(I1) ;
hold on
plot(BB(:,1),BB(:,2),'r')

Related

Algorithm to get all zeniths crossing a box in three dimensions, MATLAB implementation

I have a 3D geometry problem and I am not certain of the best approach to solve it. I have a model with two boxes, one above the others. They have the same dimension, L (length) * p (depth) * e (thickness), and are separated by a height of h. They are perfectly superposed, with no offset between them.
For each point of my bottom box, I want to get the zenith of all lines that can cross the top box and arrive to this point. It doesn't matter if the line crosses the top box by the top or the side.
The zenith is the angle of "looking up". In our case, a zenith of 0 represents the point directly above the point P, and an angle of 90 is directly looking in front. A zenith of 180 would be looking below the point, but for our use, it's useless. The zeniths we look for are between 0 and 90°.
For a more intuitive visualization, let's say that I have a hole in the ceiling, and that I want to map the zenith of all light that crosses this hole and reaches the floor.
This is what it looks like:
For any point P of the bottom box, I want an array containing the zeniths of all "rays" that cross the top box before arriving on P. The red lines are the "edges", the last zeniths I would get for each corner.
I am working on a way to code it in MATLAB and I was wondering if there was a better algorithm that I am not seeing. My approach, in pseudocode, would be this:
bottomBox = [1:L, 1:p, 1:e];
topBox = [1:L, 1:p, 1+h:e+h];
results = zeros(L:p) * NaN; % Array of results, one per "case" on the bottom box
zeniths = zeros(L:p) * NaN; % Array of zeniths for each result case
for i = 1:L
for j = 1:p % Browsing the bottom box case by case
for k = 1:L
for l = 1:p
for m = 1:e % For each bottom box case, browsing the top box case by case
p1 = topBox(k,l,m); % p1 is each case on the top box
p2 = bottomBox(i,j,1); % p2 is the current bottom box case, z doesn't mattter
p3 = topBox(i,j,m); % p3 is the projection of p2 on the top box (zenith = 0)
v1 = p1 - p2;
v2 = p3 - p2;
zeniths(k,l) = rad2deg(atan2(norm(cross(p1, p2)), dot(p1, p2)));
end
end
end
results(i,j) = zeniths;
end
end
I tried to implement this and I couldn't get it to work. More specifically, the angle calculation doesn't seem to work, I have an error stating:
Error using cross;
A and B must be of length 3 in the dimension in which the cross product is taken.
I am looking for advice on how to build the algorithm.
Please tell me if the question is better suited for another StackExchange community, such as Math.
I'll get you started showing you one way to do it for 1 point and I'll let you build the final loop to do the calc for all your points.
As expressed in the comment, for the purpose of these calculations, you do not need to consider the thickness of your plates, you can model them simply with two parallel planes separated by a distance H.
I don't know the size of your plates nor the grid size you want so I'll keep it simple for this example:
H = 5 ; % distance between the planes
[X,Y] = meshgrid(-3:3,-2:2) ;
GridSize = size(X) ;
Zb = zeros(GridSize) ;
Zt = zeros(GridSize) + H ;
This gives you 4 matrices, defining 2 planes. The bottom plane is composed of [X,Y,Zb] and the top plane is formed by [X,Y,Zt].
If you want to visualise them, you can run the following code (optional):
%% Display planes
figure ;
ht = surf(X,Y,Zt, 'FaceColor',[.8 .8 .8],'DisplayName','Top plate') ;
hold on
hb = surf(X,Y,Zb, 'FaceColor',[.6 .6 .6],'DisplayName','Bottom plate') ;
xlabel('X') ; ylabel('Y') ; zlabel('Z') ;
axis equal ; legend show
Now for the rest of the example, I selected a point P, at coordinate [-2,1,0]. This choice is completely arbitrary, just for the example. In your final algorythm you will still have to loop over several points Pi (although remember that your problem is symetric so if your domain is too large you can reduce your computations by using the symetries of your model).
%% This will have to be embedded into a loop over the points Pi
% Assuming points P=(-2,1,0)
p = [-2;1;0] ;
zn = [0;0;1] ; % unitary vector, oriented Oz
dx = X - p(1) ; % `x` distance between all points of the plane and P
dy = Y - p(2) ; % `y` distance between all points of the plane and P
dz = zeros(size(X)) + H ; % `z` distance (all the same)
V = [dx(:) dy(:) dz(:)].' ; % to obtain list of vector V = [dx;dy;dz] ;
nv = size(V,2) ; % number of points/angle to calculate
zenith = zeros(nv,1) ; % preallocate result matrix (always good!)
for k=1:nv
% [u] is the vector going from `P` to the current point considered on the top plane
u = V(:,k) ;
% determine the angle between [u] and [zn]
zenith(k) = atan2( norm(cross(u,zn)) , dot(u,zn) ) ;
end
% Reshape "zenith" from vector to matrix so it matches the base grid system
zenith = reshape( zenith , GridSize ) ;
You now have, for this point P, a matrix of angle with every other point of the top plane:
>> rad2deg(zenith)
ans =
32.31 30.96 32.31 35.80 40.32 45.00 49.39
24.09 21.80 24.09 29.50 35.80 41.81 47.12
15.79 11.31 15.79 24.09 32.31 39.51 45.56
11.31 0 11.31 21.80 30.96 38.66 45.00
15.79 11.31 15.79 24.09 32.31 39.51 45.56
Once again, completely optionally, if you want to visualise the vectors which were used for the calculations:
for k=1:nv
hp(k) = plot3([p(1) X(k)],[p(2) Y(k)],[0 H],'Marker','o','MarkerFaceColor','k') ;
end
will yield:
Now for your final result, remember you have a 2D matrix for each point P of your bottom plane, so your final result will either be a collection of 2D matrices or a large 3D matrix.
Zenith angle is just
atan2(h, sqrt(dx^2+dy^2))
where dx, dy are coordinate differences along L and p axes (i-k and j-l in your loops)
Perhaps h+m (m as your variable for m = 1:e) instead of h if you need points inside top box

How to draw repeating straight lines with specific radius and angle in matlab?

Suppose i would like to draw an image like the following:
Where the pixel values are refined to 0 for black and white for 1.
These line are drawn with specific radius and angles
Now I create a 80 x 160 matrix
texturematrix = zeros(80,160);
then i want to change particular elements to be 1 according to the lines conditions
but how do i make them repeatedly with specific distance apart from each others effectively?
Thanks a lot everyone!
This might not be what you are looking for, but generating such an image could be done by plotting a set of lines, as follows:
% grid sizes
m = 6;
n = 5;
% line length and angle
len = 1;
theta = .1*pi;
[a,b] = meshgrid(1:m,1:n);
x = reshape([a(:),a(:)+len*cos(theta),nan(numel(a),1)]',[],1);
y = reshape([b(:),b(:)+len*sin(theta),nan(numel(b),1)]',[],1);
h = figure();
plot(x,y,'k', 'LineWidth', 2);
But this has nothing to do with a texture matrix. So, we construct a matrix of desired size:
set(gca, 'position',[0 0 1 1], 'units','normalized', 'YTick',[], 'XTick',[]);
frame = frame2im(getframe(h),[0 0 1 1]);
im = imresize(frame,[80 160]);
M = ~(im(2:end,2:end,1)==255);

local histograms - recomputing histograms for all parts of the image - Matlab

i already have this:
function [h] = histImage (img)
nPix = numel(img);
h = accumarray( img (:)+1 , ones(nPix,1)/nPix, [256 1] , #sum,0)
this function will return a grayscale histogram for a given img into a 1X256 vector
now i want to build this function:
input: img - grayscale image matrix in rage [0..255]
windowSize - a 1x2 array [r c] rows and cols.
output: histArray 3d matrix for every i,j the 1D array histArray(i,j,:) is the histogram of img of the size WindowSize whose top left corner is (i,j)
function [histArray] = localHistograms (img,windowSize)
histArray = zeros(windowSize(1),WindowSize(2),256);
for i = 1:windowSize(1)
for j = 1:windowSize(2)
histArray(i,j,:) = histImage(img( i:windowSize(1), j:windowSize(2) ))
end
end
end
this is what i have so far can u please tell me my mistake?
how can i check my mistakes ? just enter some random images?
ok so my friend help me figuring out my mistake here is the working and tested code:
function [ histArray ] = localHistograms ( img,windowSize )
% Given an image returns a 3D array of histograms –
% one histogram per window in image.
% Input:
% img - a grayscale image in the range [0..255]
% windowSize – a 1x2 array [r c] defining the num rows and num cols
% of image windows.
% Output:
% histArray – an NxMx256 array.
%
% For every (i,j) the 1D array histArray(i,j,:) is the histogram
% of an image window of size windowSize whose top left corner
% pixel is (i,j). Histograms of windows that exceed the boundary
% of the of img are not included in histArray (thus N = number of
% rows of img less the number of rows of window +1.
% Similarly M=size(img,2)-windowSize(2)+1 ).
%
% Method: Scans the img pixel by pixel. For each scanned pixel,
% determines the histogram of the image window starting at the
% pixel and extending windowSize(1) rows and windowSize(2).
N = size(img,1) - windowSize(1) + 1;
M = size(img,2) - windowSize(2) + 1;
histArray = zeros(N ,M,256);
for i = 1:N
for j = 1:M
histArray(i,j,:) = histImage(img(i:i+windowSize(1)-1,j:j+windowSize(2)-1));
end
end
end

Plot images as axis labels in MATLAB

I am plotting a 7x7 pixel 'image' in MATLAB, using the imagesc command:
imagesc(conf_matrix, [0 1]);
This represents a confusion matrix, between seven different objects. I have a thumbnail picture of each of the seven objects that I would like to use as the axes tick labels. Is there an easy way to do this?
I don't know an easy way. The axes properties XtickLabel which determines the labels, can only be strings.
If you want a not-so-easy way, you could do something in the spirit of the following non-complete (in the sense of a non-complete solution) code, creating one label:
h = imagesc(rand(7,7));
axh = gca;
figh = gcf;
xticks = get(gca,'xtick');
yticks = get(gca,'ytick');
set(gca,'XTickLabel','');
set(gca,'YTickLabel','');
pos = get(axh,'position'); % position of current axes in parent figure
pic = imread('coins.png');
x = pos(1);
y = pos(2);
dlta = (pos(3)-pos(1)) / length(xticks); % square size in units of parant figure
% create image label
lblAx = axes('parent',figh,'position',[x+dlta/4,y-dlta/2,dlta/2,dlta/2]);
imagesc(pic,'parent',lblAx)
axis(lblAx,'off')
One problem is that the label will have the same colormap of the original image.
#Itmar Katz gives a solution very close to what I want to do, which I've marked as 'accepted'. In the meantime, I made this dirty solution using subplots, which I've given here for completeness. It only works up to a certain size input matrix though, and only displays well when the figure is square.
conf_mat = randn(5);
A = imread('peppers.png');
tick_images = {A, A, A, A, A};
n = length(conf_mat) + 1;
% plotting axis labels at left and top
for i = 1:(n-1)
subplot(n, n, i + 1);
imshow(tick_images{i});
subplot(n, n, i * n + 1);
imshow(tick_images{i});
end
% generating logical array for where the confusion matrix should be
idx = 1:(n*n);
idx(1:n) = 0;
idx(mod(idx, n)==1) = 0;
% plotting the confusion matrix
subplot(n, n, find(idx~=0));
imshow(conf_mat);
axis image
colormap(gray)

MATLAB: Drawing a line over a black and white image

What is the best way to draw a line over a black and white (binary) image in MATLAB, provided the start and end coordinates are known?
Please note, I am not trying to add an annotation line. I would like the line to become part of the image.
You may want to look at my answer to an SO question about adding a line to an image matrix. Here's a similar example to the one I have in that answer, which will make a white line running from row and column index (10, 10) to (240, 120):
img = imread('cameraman.tif'); % Load a sample black and white image
x = [10 240]; % x coordinates
y = [10 120]; % y coordinates
nPoints = max(abs(diff(x)), abs(diff(y)))+1; % Number of points in line
rIndex = round(linspace(y(1), y(2), nPoints)); % Row indices
cIndex = round(linspace(x(1), x(2), nPoints)); % Column indices
index = sub2ind(size(img), rIndex, cIndex); % Linear indices
img(index) = 255; % Set the line points to white
imshow(img); % Display the image
And here's the resulting image:
If you are bothered by exceptional cases of other methods here's a bullet-proof method that results in a line:
whose pixels always touch each other during the whole length of the line (pixels are 8-neighbors to each other),
density of the line is not dependent on the additional parameter, but is determined flexibly to accommodate guarantee from the first point.
Inputs (convenient for making function out of this code):
img - matrix that contains image,
x1, y1, x2, y2 - coordinates of the end points of the line to be drawn.
Code:
% distances according to both axes
xn = abs(x2-x1);
yn = abs(y2-y1);
% interpolate against axis with greater distance between points;
% this guarantees statement in the under the first point!
if (xn > yn)
xc = x1 : sign(x2-x1) : x2;
yc = round( interp1([x1 x2], [y1 y2], xc, 'linear') );
else
yc = y1 : sign(y2-y1) : y2;
xc = round( interp1([y1 y2], [x1 x2], yc, 'linear') );
end
% 2-D indexes of line are saved in (xc, yc), and
% 1-D indexes are calculated here:
ind = sub2ind( size(img), yc, xc );
% draw line on the image (change value of '255' to one that you need)
img(ind) = 255;
Here's the example image with three lines drawn on it:
This algorithm offers one approach.
It actually is just a modification on plesiv's answer. I'm drawing thousands of lines over an image and I need to increase the performance. The most improvement made by omitting interp1 calls and using integer variables made it slightly faster. It performs about 18% faster on my PC comparing to plesiv's code.
function img = drawLine(img, x1, y1, x2, y2)
x1=int16(x1); x2=int16(x2); y1=int16(y1); y2=int16(y2);
% distances according to both axes
xn = double(x2-x1);
yn = double(y2-y1);
% interpolate against axis with greater distance between points;
% this guarantees statement in the under the first point!
if (abs(xn) > abs(yn))
xc = x1 : sign(xn) : x2;
if yn==0
yc = y1+zeros(1, abs(xn)+1, 'int16');
else
yc = int16(double(y1):abs(yn/xn)*sign(yn):double(y2));
end
else
yc = y1 : sign(yn) : y2;
if xn==0
xc = x1+zeros(1, abs(yn)+1, 'int16');
else
xc = int16(double(x1):abs(xn/yn)*sign(xn):double(x2));
end
end
% 2-D indexes of line are saved in (xc, yc), and
% 1-D indexes are calculated here:
ind = sub2ind(size(img), yc, xc);
% draw line on the image (change value of '255' to one that you need)
img(ind) = 255;
end
If you have the Computer Vision System Toolbox, you can use insertShape.

Resources