Rotate a spot in a binary image by 45/-45 degree

Hey Community!
I was wondering how I can rotate a specific rect / object in a binary image.
Let me explain!
At first I have a black matrix with an example size of 7x20. I put some ones into it like:
% img => 7x20 logical
midlerow = 4;
midlecol = 10;
witdh = 2;
whalf = 2/2;
hight = 6;
hhalf = 6/2;
img( (midlerow-whalf):(midlerow+whalf), (midlecol-hhalf):(midlecol+hhalf) ) = 1;
Lets say i the matrix looks like this afterwards:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
So the ones represent a white spot in a black image - looking like a recangular
Is there a way to rotate only this area consisting of ones, just by either 45/-45 or 135 / - 135 degree? Like a automatic way that is similar to my code?
As a furhter example:
I rotate by 90/-90 degree as follows:
img( (midlerow-hhalf):(midlerow+hhalf), (midlecol-whalf):(midlecol+whalf) ) = 1;
0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0
Thanks & best regards!
/edit: it should also work in simulink

 Respuesta aceptada

Tommy
Tommy el 29 de Abr. de 2020
Editada: Tommy el 29 de Abr. de 2020
Quick note: I started working on this, then stepped away for a bit, then came back and finished. Despite ImageAnalyst's great answer which came sometime in the interim, I wanted to post anyway because I did have fun with it and I figured someone else might find it interesting!
--------------------------------------------------------------------------------------------
I had a bit of fun with this. Trying to rotate this image by 45 degrees:
img = false(70, 200);
midlerow = 36;
midlecol = 100;
witdh = 20;
whalf = 20/2;
hight = 60;
hhalf = 60/2;
img( (midlerow-whalf):(midlerow+whalf), (midlecol-hhalf):(midlecol+hhalf) ) = 1;
imshow(img);
(1) First I tried pulling out the coordinates of the white portion, shifting them so that their center is (0,0), multiplying by a rotation matrix, and then shifting the new coordinates back to where they came from:
[row, col] = ind2sub(size(img), find(img)); % get coordinates of white portion
pos = [row-midlerow, col-midlecol]; % move center to (0,0)
R = @(angle) [cosd(angle) -sind(angle); sind(angle) cosd(angle)]; % rotation matrix
newPos = round(R(45)*pos') + [midlerow; midlecol]; % multiply and reposition new coordinates
newImg = false(70, 200);
for i = 1:size(newPos,2)
newImg(newPos(1,i), newPos(2,i)) = true; % fill new image
end
figure; imshow(newImg);
Okay but not great. The (mathematical) image of our (actual) image under the above function does not include every point we'd like it to include. A given point [at, say, (2,3)] is mapped to a new point which almost definitely contains non-integer coordinates [for example (2,3) is mapped to (-0.7071,3.5355)], so we have to pick a valid nearby point. The above uses round to do this [so that (2,3) is ultimately mapped to (-1,4)], and this means that some points are never reached.
(2) I tried again using both ceil and floor on each input to generate twice the number of outputs [mapping (2,3) to both (0,4) and (-1,3), for example]:
[row, col] = ind2sub(size(img), find(img));
pos = [row-midlerow, col-midlecol];
R = @(angle) [cosd(angle) -sind(angle); sind(angle) cosd(angle)];
newPos = [floor(R(45)*pos') ceil(R(45)*pos')] + [midlerow; midlecol];
newImg = false(70, 200);
for i = 1:size(newPos,2)
newImg(newPos(1,i), newPos(2,i)) = true;
end
figure; imshow(newImg);
Better I guess, but not perfect. I'll bet using every possible combination (ceil on the x coordinate and floor on the y, ceil on the y and floor on the x, etc) might get you there... but that seems silly.
(3) A higher resolution of input points would work:
[row, col] = ind2sub(size(img), find(img));
[X,Y] = meshgrid(linspace(min(row)-midlerow, max(row)-midlerow, 1000),...
linspace(min(col)-midlecol, max(col)-midlecol, 1000));
betterPos = [X(:), Y(:)];
R = @(angle) [cosd(angle) -sind(angle); sind(angle) cosd(angle)];
newPos = ceil(R(45)*betterPos') + [midlerow; midlecol];
newImg = false(70, 200);
for i = 1:size(newPos,2)
newImg(newPos(1,i), newPos(2,i)) = true;
end
figure; imshow(newImg);
(4) imrotate deals with all the messy stuff, but it rotates entire images. You could cut the relevant portion of the image, rotate it, and then replace it:
diag = ceil(sqrt(witdh^2 + hight^2));
half = floor(diag/2);
rotImg = img( (midlerow-half):(midlerow+half), (midlecol-half):(midlecol+half) );
rotImg = imrotate(rotImg, 45, 'crop');
newImg = false(70, 200);
newImg( (midlerow-half):(midlerow+half), (midlecol-half):(midlecol+half) ) = rotImg;
figure; imshow(newImg);
That probably gives the best result yet, but I used 'crop' so that the rotated portion would stay the same size, and therefore I took care to make sure that the part which I did rotate was big enough so that none of the white portion was cropped away.
Conclusion: I think the fact that the test image is a simple rectangle made this a lot easier in each case. The general idea should still apply (mapping points to new points using a rotation matrix), but it would be harder to generate a fine resolution input (in the case of #3) or determine what portion of your image to cut out and pass to imrotate (in the case of #4).
EDIT
Expanding on #3 (greater resolution input) for a not-as-nice (but still pretty nice) image:
img = false(70, 200);
midlerow = 36;
midlecol = 100;
witdh = 20;
whalf = 20/2;
hight = 60;
hhalf = 60/2;
img( (midlerow-whalf):(midlerow+whalf), (midlecol-hhalf):(midlecol+hhalf) ) = 1;
img( (midlerow-whalf):(midlerow+whalf), (midlecol-hhalf/2):(midlecol+hhalf/2) ) = 0;
imshow(img);
[N, M] = size(img);
#2 (and #1) has the same problem as before:
[row, col] = ind2sub(size(img), find(img));
pos = [row-midlerow, col-midlecol];
R = @(angle) [cosd(angle) -sind(angle); sind(angle) cosd(angle)];
newPos = [floor(R(45)*pos') ceil(R(45)*pos')] + [midlerow; midlecol];
newImg = false(N, M);
for i = 1:size(newPos,2)
newImg(newPos(1,i), newPos(2,i)) = true;
end
figure; imshow(newImg);
#3 no longer works, because of how I set up the meshgrid using linspace from min to max (which basically assumes a rectangle):
[row, col] = ind2sub(size(img), find(img));
[X,Y] = meshgrid(linspace(min(row)-midlerow, max(row)-midlerow, 1000),...
linspace(min(col)-midlecol, max(col)-midlecol, 1000));
betterPos = [X(:), Y(:)];
R = @(angle) [cosd(angle) -sind(angle); sind(angle) cosd(angle)];
newPos = ceil(R(45)*betterPos') + [midlerow; midlecol];
newImg = false(N, M);
for i = 1:size(newPos,2)
newImg(newPos(1,i), newPos(2,i)) = true;
end
figure; imshow(newImg);
So instead, how about using imresize to achieve a higher resolution input:
factor = 10;
bigImg = imresize(img, factor);
[row, col] = ind2sub(size(bigImg), find(bigImg));
pos = [row-factor*midlerow, col-factor*midlecol];
R = @(angle) [cosd(angle) -sind(angle); sind(angle) cosd(angle)];
newPos = ceil(R(45)*pos') + factor*[midlerow; midlecol];
newImg = false(factor*N, factor*M);
for i = 1:size(newPos,2)
newImg(newPos(1,i), newPos(2,i)) = true;
end
newImg = imresize(newImg, 1/factor);
figure; imshow(newImg);
Nice. A factor of 10 and the default resizing method ('bicubic') was good enough for this example, but I doubt that that's always the case.
However, imrotate will probably almost always beat trying to transform the image yourself. I would seriously consider the ideas brought up in Image Analyst's answer.

7 comentarios

Domi
Domi el 29 de Abr. de 2020
Editada: Domi el 29 de Abr. de 2020
Wow, I am overhelmed. Will pick upon your conclusion tomorrow morning. After my post I tried to do it by hand, bit it's a lot of code and seems totally silly. I guess I will go with (2) because the lower the computation, the better. Im usimg a Raspberry running all the image processing stuff of my project.
Key is to keep the middle coordinate und work with it because it changes over time. The rectancle needs to be 1 in the whole area..
I am doing a mapping by camera of an area with moving mobile robot. My map is basically a binary image. Because of the image processing the robot is marked as an object with binary one.. its shape is nearly a rectancle that's why I picked the example.
This helps me out! thank you!
Tommy
Tommy el 29 de Abr. de 2020
Happy to help!
I thought a bit more about how to achieve the better resolution input for a more general case. I hope it does not overwhelm you! I was just unsatisfied with how I generated the higher resolution at first, and the new example shows why.
Let me know if you'd like help with anything specific. I am happy to think more about how to use imrotate here if you'd like.
Domi
Domi el 30 de Abr. de 2020
Editada: Domi el 30 de Abr. de 2020
Hey there,
do you know how to get (2)
[row, col] = ind2sub(size(img), find(img));
pos = [row-midlerow, col-midlecol];
R = @(angle) [cosd(angle) -sind(angle); sind(angle) cosd(angle)];
newPos = [floor(R(45)*pos') ceil(R(45)*pos')] + [midlerow; midlecol];
newImg = false(70, 200);
for i = 1:size(newPos,2)
newImg(newPos(1,i), newPos(2,i)) = true;
end
figure; imshow(newImg);
a bit more simplier? I want to run it on a live image in simulink
My previous code looked like this one. Where GlobPose is just the same as a middle coordinate and Theta is the rotation.
% image is 78x200
vGesRobi = 12;
uGesRobi = 18;
vRob = vGesRobi/2;
uRob = uGesRobi/2;
vInflate = 2;
uInflate = 3;
% Add together
vAdd = vRob + vInflate; % 8
uAdd = uRob + uInflate; % 12
% init area
u1 = GlobPose(1,1) - uAdd;
u2 = GlobPose(1,1) + uAdd;
v1 = GlobPose(1,2) - vAdd;
v2 = GlobPose(1,2) + vAdd;
u11 = GlobPose(1,1) - vadd;
u12 = GlobPose(1,1) + vadd;
v11 = GlobPose(1,2) - uAdd;
v12 = GlobPose(1,2) + uAdd;
% Area needs to change with rotation (!)
Theta = estPose(3); % rotation
% Rotationcases
if (Theta == 0) % case 1
map(v1:v2, u1:u2) = 0;
elseif (Theta == 45) % case2
map() = 0; % ???
elseif (Theta == 90) % case3 -
map(v11:v12, u11:u12) = 0;
elseif (Theta == 135) % case4
map() = 0; % ???
elseif (Theta == -45) % case5
map() = 0; % ???
elseif (Theta == -90) % case6
map(v11:v12, u11:u12) = 0;
Domi
Domi el 30 de Abr. de 2020
Editada: Domi el 30 de Abr. de 2020
I was thinking to create diagonals for each side and fill it up..sth like:
0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0
0 0 0 0 1 1 1 0 0
0 0 0 1 1 1 1 1 0
0 0 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1
0 0 0 0 1 1 1 1 1
0 0 0 0 0 1 1 1 1
But I do not know a clever way to achieve it either than by doing it the lines by hand which is kinda silly..
Tommy
Tommy el 3 de Mayo de 2020
Sorry, this slipped through the cracks somehow! If I am understanding this correctly, you only need to rotate in increments of 45 degrees? Would you be able to provide the shape of this robot?
Domi
Domi el 3 de Mayo de 2020
I respect the approaches you wrote here but i can not implement in Simulink with my rapsberry because of the computing time as well as the lack of support of the functions from matlab .. such as find etc.
I guess you can see the robot as a rect with 15px width and 20px height in thr image. His shape is not realy like this but it works out for the case of 0,180 as well as 90/-90 to cover him with "free space" of bin 0 in the binary image (map)
Domi
Domi el 3 de Mayo de 2020
Image is still 78x120px. It's a live image from a camera which gets trough some image processing like edge detection & inflating and creates the binary image. Nonetheless the robot is seen as an white area (binary ones) which lets my algorithm think he is some kind of object..

Iniciar sesión para comentar.

Más respuestas (1)

Image Analyst
Image Analyst el 29 de Abr. de 2020
Simple? No. When you rotate, your canvass enlarges. So you need to first say if you want the canvass to enlarge or do you want to clip corners that pivot out of the original image boundaries. Then you need to say what your center of rotation is. imrotate() doesn't let you specify center of rotation so you'll have to do it yourself with the rotation matrix (look it up on Wikipedia). Then you'll have to paste it back onto the canvass at the right location. You'd have to figure out what the bounding box is after you make sure that your rotation point is not shifted. It's somewhat easier if you have a binary image (0 and 1) because you can use the 'Image' and 'Centroid' properties of regionprops to get the bounding box and centroid.
Here's a start:
props = regionprops(binaryImage, 'Image', 'Centroid');
for k = 1 : length(props)
thisImage = props(k).Image;
xCenter = props(k).Centroid(1);
yCenter = props(k).Centroid(2);
rotatedImage = imrotate(thisImage, 135, 'nearest', 'loose');
% Now paste it back on.
% However it gets tricky if the blob rotated out of the bounds of the original image!!!
end

Categorías

Más información sobre Computer Vision Toolbox en Centro de ayuda y File Exchange.

Preguntada:

el 29 de Abr. de 2020

Comentada:

el 3 de Mayo de 2020

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by