Related
I need to construct new image based on coordinates representing pixels from original image. I got three arrays: one for new image, one for coordinates and one for original image. Arrays are shape of (480, 640, 3) except coordinate array which is (480, 640, 2). Here is my code.. it get's the job done, but it's very slow (fps ~1,8) for my purposes
for i in range(height):
for j in range(width):
y, x = coordinateMap.item((i, j, 0)), coordinateMap.item((i, j, 1))
if y < 0 or y >= height or x < 0 or x >= width:
value = (0,0,0)
else:
value = originalImage[y][x]
newImage[i][j] = value
Is there way to get rid of that nested loop?
I have an image (white background with 1-5 black dots) that is called main.jpg (main image).
I am trying to place another image (secondary.jpg) in every black dot that is found in main image.
In order to do that:
I found the black pixels in main image
resize the secondary image to specific size that I want
plot the image in every coordinate that I found in step one. (the black pixel should be the center coordinates of the secondary image)
Unfortunately, I don't know how to do the third step.
for example:
main image is:
secondary image is:
output:
(The dots are behind the chairs. They are the image center points)
This is my code:
mainImage=imread('main.jpg')
secondaryImage=imread('secondary.jpg')
secondaryImageResized = resizeImage(secondaryImage)
[m n]=size(mainImage)
for i=1:n
for j=1:m
% if it's black pixel
if (mainImage(i,j)==1)
outputImage = plotImageInCoordinates(secondaryImageResized, i, j)
% save this image
imwrite(outputImage,map,'clown.bmp')
end
end
end
% resize the image to (250,350) width, height
function [ Image ] = resizeImage(img)
image = imresize(img, [250 350]);
end
function [outputImage] = plotImageInCoordinates(image, x, y)
% Do something
end
Any help appreciated!
Here's an alternative without convolution. One intricacy that you must take into account is that if you want to place each image at the centre of each dot, you must determine where the top left corner is and index into your output image so that you draw the desired object from the top left corner to the bottom right corner. You can do this by taking each black dot location and subtracting by half the width horizontally and half the height vertically.
Now onto your actual problem. It's much more efficient if you loop through the set of points that are black, not the entire image. You can do this by using the find command to determine the row and column locations that are 0. Once you do this, loop through each pair of row and column coordinates, do the subtraction of the coordinates and then place it on the output image.
I will impose an additional requirement where the objects may overlap. To accommodate for this, I will accumulate pixels, then find the average of the non-zero locations.
Your code modified to accommodate for this is as follows. Take note that because you are using JPEG compression, you will have compression artifacts so regions that are 0 may not necessarily be 0. I will threshold with an intensity of 128 to ensure that zero regions are actually zero. You will also have the situation where objects may go outside the boundaries of the image. Therefore to accommodate for this, pad the image sufficiently with twice of half the width horizontally and twice of half the height vertically then crop it after you're done placing the objects.
mainImage=imread('https://i.stack.imgur.com/gbhWJ.png');
secondaryImage=imread('https://i.stack.imgur.com/P0meM.png');
secondaryImageResized = imresize(secondaryImage, [250 300]);
% Find half height and width
rows = size(secondaryImageResized, 1);
cols = size(secondaryImageResized, 2);
halfHeight = floor(rows / 2);
halfWidth = floor(cols / 2);
% Create a padded image that contains our main image. Pad with white
% pixels.
rowsMain = size(mainImage, 1);
colsMain = size(mainImage, 2);
outputImage = 255*ones([2*halfHeight + rowsMain, 2*halfWidth + colsMain, size(mainImage, 3)], class(mainImage));
outputImage(halfHeight + 1 : halfHeight + rowsMain, ...
halfWidth + 1 : halfWidth + colsMain, :) = mainImage;
% Find a mask of the black pixels
mask = outputImage(:,:,1) < 128;
% Obtain black pixel locations
[row, col] = find(mask);
% Reset the output image so that they're all zeros now. We use this
% to output our final image. Also cast to ensure accumulation is proper.
outputImage(:) = 0;
outputImage = double(outputImage);
% Keeps track of how many times each pixel was hit by the object
% This is so that we can find the average at each location.
counts = zeros([size(mask), size(mainImage, 3)]);
% For each row and column location in the image
for i = 1 : numel(row)
% Get the row and column locations
r = row(i); c = col(i);
% Offset to get the top left corner
r = r - halfHeight;
c = c - halfWidth;
% Place onto final image
outputImage(r:r+rows-1, c:c+cols-1, :) = outputImage(r:r+rows-1, c:c+cols-1, :) + double(secondaryImageResized);
% Accumulate the counts
counts(r:r+rows-1,c:c+cols-1,:) = counts(r:r+rows-1,c:c+cols-1,:) + 1;
end
% Find average - Any values that were not hit, change to white
outputImage = outputImage ./ counts;
outputImage(counts == 0) = 255;
outputImage = uint8(outputImage);
% Now crop and show
outputImage = outputImage(halfHeight + 1 : halfHeight + rowsMain, ...
halfWidth + 1 : halfWidth + colsMain, :);
close all; imshow(outputImage);
% Write the final output
imwrite(outputImage, 'finalimage.jpg', 'Quality', 100);
We get:
Edit
I wasn't told that your images had transparency. Therefore what you need to do is use imread but ensure that you read in the alpha channel. We then check to see if one exists and if one does, we will ensure that the background of any values with no transparency are set to white. You can do that with the following code. Ensure this gets placed at the very top of your code, replacing the images being loaded in:
mainImage=imread('https://i.stack.imgur.com/gbhWJ.png');
% Change - to accommodate for transparency
[secondaryImage, ~, alpha] = imread('https://i.imgur.com/qYJSzEZ.png');
if ~isempty(alpha)
m = alpha == 0;
for i = 1 : size(secondaryImage,3)
m2 = secondaryImage(:,:,i);
m2(m) = 255;
secondaryImage(:,:,i) = m2;
end
end
secondaryImageResized = imresize(secondaryImage, [250 300]);
% Rest of your code follows...
% ...
The code above has been modified to read in the basketball image. The rest of the code remains the same and we thus get:
You can use convolution to achieve the desired effect. This will place a copy of im everywhere there is a black dot in imz.
% load secondary image
im = double(imread('secondary.jpg'))/255.0;
% create some artificial image with black indicators
imz = ones(500,500,3);
imz(50,50,:) = 0;
imz(400,200,:) = 0;
imz(200,400,:) = 0;
% create output image
imout = zeros(size(imz));
imout(:,:,1) = conv2(1-imz(:,:,1),1-im(:,:,1),'same');
imout(:,:,2) = conv2(1-imz(:,:,2),1-im(:,:,2),'same');
imout(:,:,3) = conv2(1-imz(:,:,3),1-im(:,:,3),'same');
imout = 1-imout;
% output
imshow(imout);
Also, you probably want to avoid saving main.jpg as a .jpg since it results in lossy compression and will likely cause issues with any method that relies on exact pixel values. I would recommend using .png which is lossless and will also likely compress better than .jpg for synthetic images where the same colors repeat many times.
Currently I have been working on obtaining the length of a curve, with the following code I have managed to get the length of a curve present in an image.
test image one curve
Then I paste the code that I used to get the length of the curve of a simple image. What I did is the following:
I got the columns and rows of the image
I got the columns in x and the rows in y
I obtained the coefficients of the curve, based on the formula of the
parable
Build the equation
Implement the arc length formula to obtain the length of the curve
grayImage = imread(fullFileName);
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
subplot(2, 2, 1);
imshow(grayImage, []);
% Get the rows (y) and columns (x).
[rows, columns] = find(binaryImage);
coefficients = polyfit(columns, rows, 2); % Gets coefficients of the formula.
% Fit a curve to 500 points in the range that x has.
fittedX = linspace(min(columns), max(columns), 500);
% Now get the y values.
fittedY = polyval(coefficients, fittedX);
% Plot the fitting:
subplot(2,2,3:4);
plot(fittedX, fittedY, 'b-', 'linewidth', 4);
grid on;
xlabel('X', 'FontSize', fontSize);
ylabel('Y', 'FontSize', fontSize);
% Overlay the original points in red.
hold on;
plot(columns, rows, 'r+', 'LineWidth', 2, 'MarkerSize', 10)
formula = poly2sym([coefficients(1),coefficients(2),coefficients(3)]);
% formulaD = vpa(formula)
df=diff(formula);
df = df^2;
f= (sqrt(1+df));
i = int(f,min(columns),max(columns));
j = double(i);
disp(j);
Now I have the image 2 which has n curves, I do not know how I can do to get the length of each curve
test image n curves
I suggest you to look at Hough Transformation:
https://uk.mathworks.com/help/images/hough-transform.html
You will need Image Processing Toolbox. Otherwise, you have to develop your own logic.
https://en.wikipedia.org/wiki/Hough_transform
Update 1
I had a two-hour thinking about your problem and I'm only able to extract the first curve. The problem is to locate the starting points of the curves. Anyway, here is the code I come up with and hopefully will give you some ideas for further development.
clc;clear;close all;
grayImage = imread('2.png');
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
% find edge.
bw = edge(grayImage,'canny');
imshow(bw);
[x, y] = find(bw == 1);
P = [x,y];
% For each point, find a point that is of distance 1 or sqrt(2) to it, i.e.
% find its connectivity.
cP = cell(1,length(x));
for i = 1:length(x)
px = x(i);
py = y(i);
dx = x - px*ones(size(x));
dy = y - py*ones(size(y));
distances = (dx.^2 + dy.^2).^0.5;
cP{i} = [x(distances == 1), y(distances == 1);
x(distances == sqrt(2)), y(distances == sqrt(2))];
end
% pick the first point and a second point that is connected to it.
fP = P(1,:);
Q(1,:) = fP;
Q(2,:) = cP{1}(1,:);
m = 2;
while true
% take the previous point from point set Q, when current point is
% Q(m,1)
pP = Q(m-1,:);
% find the index of the current point in point set P.
i = find(P(:,1) == Q(m,1) & P(:,2) == Q(m,2));
% Find the distances from the previous points to all points connected
% to the current point.
dx = cP{i}(:,1) - pP(1)*ones(length(cP{i}),1);
dy = cP{i}(:,2) - pP(2)*ones(length(cP{i}),1);
distances = (dx.^2 + dy.^2).^0.5;
% Take the farthest point as the next point.
m = m+1;
p_cache = cP{i}(find(distances==max(distances),1),:);
% Calculate the distance of this point to the first point.
distance = ((p_cache(1) - fP(1))^2 + (p_cache(2) - fP(2))^2).^0.5;
if distance == 0 || distance == 1
break;
else
Q(m,:) = p_cache;
end
end
% By now we should have built the ordered point set Q for the first curve.
% However, there is a significant weakness and this weakness prevents us to
% build the second curve.
Update 2
Some more work since the last update. I'm able to separate each curve now. The only problem I can see here is to have a good curve fitting. I would suggest B-spline or Bezier curves than polynomial fit. I think I will stop here and leave you to figure out the rest. Hope this helps.
Note that the following script uses Image Processing Toolbox to find the edges of the curves.
clc;clear;close all;
grayImage = imread('2.png');
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
% find edge.
bw = edge(grayImage,'canny');
imshow(bw);
[x, y] = find(bw == 1);
P = [x,y];
% For each point, find a point that is of distance 1 or sqrt(2) to it, i.e.
% find its connectivity.
cP =[0,0]; % add a place holder
for i = 1:length(x)
px = x(i);
py = y(i);
dx = x - px*ones(size(x));
dy = y - py*ones(size(y));
distances = (dx.^2 + dy.^2).^0.5;
c = [find(distances == 1); find(distances == sqrt(2))];
cP(end+1:end+length(c),:) = [ones(length(c),1)*i, c];
end
cP (1,:) = [];% remove the place holder
% remove duplicates
cP = unique(sort(cP,2),'rows');
% seperating curves
Q{1} = cP(1,:);
for i = 2:length(cP)
cp = cP(i,:);
% search for points in cp in Q.
for j = 1:length(Q)
check = ismember(cp,Q{j});
if ~any(check) && j == length(Q) % if neither has been saved in Q
Q{end+1} = cp;
break;
elseif sum(check) == 2 % if both points cp has been saved in Q
break;
elseif sum(check) == 1 % if only one of the points exists in Q, add the one missing.
Q{j} = [Q{j}, cp(~check)];
break;
end
end
% review sets in Q, merge the ones having common points
for j = 1:length(Q)-1
q = Q{j};
for m = j+1:length(Q)
check = ismember(q,Q{m});
if sum(check)>=1 % if there are common points
Q{m} = [Q{m}, q(~check)]; % merge
Q{j} = []; % delete the merged set
break;
end
end
end
Q = Q(~cellfun('isempty',Q)); % remove empty cells;
end
% each cell in Q represents a curve. Note that points are not ordered.
figure;hold on;axis equal;grid on;
for i = 1:length(Q)
x_ = x(Q{i});
y_ = y(Q{i});
coefficients = polyfit(y_, x_, 3); % Gets coefficients of the formula.
% Fit a curve to 500 points in the range that x has.
fittedX = linspace(min(y_), max(y_), 500);
% Now get the y values.
fittedY = polyval(coefficients, fittedX);
plot(fittedX, fittedY, 'b-', 'linewidth', 4);
% Overlay the original points in red.
plot(y_, x_, 'r.', 'LineWidth', 2, 'MarkerSize', 1)
formula = poly2sym([coefficients(1),coefficients(2),coefficients(3)]);
% formulaD = vpa(formula)
df=diff(formula);
lengthOfCurve(i) = double(int((sqrt(1+df^2)),min(y_),max(y_)));
end
Result:
You can get a good approximation of the arc lengths using regionprops to estimate the perimeter of each region (i.e. arc) and then dividing that by 2. Here's how you would do this (requires the Image Processing Toolbox):
img = imread('6khWw.png'); % Load sample RGB image
bw = ~imbinarize(rgb2gray(img)); % Convert to grayscale, then binary, then invert it
data = regionprops(bw, 'PixelList', 'Perimeter'); % Get perimeter (and pixel coordinate
% list, for plotting later)
lens = [data.Perimeter]./2; % Compute lengths
imshow(bw) % Plot image
hold on;
for iLine = 1:numel(data),
xy = mean(data(iLine).PixelList); % Get mean of coordinates
text(xy(1), xy(2), num2str(lens(iLine), '%.2f'), 'Color', 'r'); % Plot text
end
And here's the plot this makes:
As a sanity check, we can use a simple test image to see how good an approximation this gives us:
testImage = zeros(100); % 100-by-100 image
testImage(5:95, 5) = 1; % Add a vertical line, 91 pixels long
testImage(5, 10:90) = 1; % Add a horizontal line, 81 pixels long
testImage(2020:101:6060) = 1; % Add a diagonal line 41-by-41 pixels
testImage = logical(imdilate(testImage, strel('disk', 1))); % Thicken lines slightly
Running the above code on this image, we get the following:
As you can see the horizontal and vertical line lengths come out close to what we expect, and the diagonal line is a little bit more than sqrt(2)*41 due to the dilation step extending its length slightly.
I try with this post but i donĀ“t understand so much, but the idea Colours123 sounds great, this post talk about GUI https://www.mathworks.com/matlabcentral/fileexchange/24195-gui-utility-to-extract-x--y-data-series-from-matlab-figures
I think that you should go through the image and ask if there is a '1' if yes, ask the following and thus identify the beginning of a curve, get the length and save it in a BD, I am not very good with the code , But that's my idea
I know there are a lot of questions alrady answered about this. However, mine varies slightly. Whenever we implement the smooth coloring algorithim as I understand it.
mu = 1 + n + math.log2(math.log2(z)) / math.log2(2)
where n is the escape iteration and 2 is the power z is to, and if im not mistaken z is the modulus of the complex number at that escape iteration. We then use this renormalized escape value in our linear interpolation between colors to produce a smoothly banded mandelbrot set. I've seen answers to other questions about this where we run this value through a HSB to RGB conversion, however I still fail to understand how this would provide a smooth gradient of colors and how to implement this in python.
However, whenever I attempted to implement this it produces floating point RGB values, but there isn't an image format that I know of, besides a .tiff file, that would support this, and if we round off to integers we still have unsmooth banding. So how is this supposed to produce a smoothly banded image if we cannot directly use the RGB values it produces? Example code of what I tried below, since I don't undertand fully how to implement this I made an attempt at a solution that somewhat produces smooth banding. This produces a somewhat smoothly banded image between two colors blue for the full set and a progressively whiter color the further we zoom in on the set to the point where at a certain depth everything just appears blurred. Since I'm using tkinter to do this I had to convert the RGB values to hex to be able to draw them to the canvas.
I;m computing the set recursively, and in my other function (not posted below) i am setting the window width and height then iterating over these for the pixels of the tkinter window and computing this recursion in the inner loop.
def linear_interp(self, color_1, color_2, i):
r = (color_1[0] * (1 - i)) + (color_2[0] * i)
g = (color_1[1] * (1 - i)) + (color_2[1] * i)
b = (color_1[2] * (1 - i)) + (color_2[2] * i)
rgb_list = [r, g, b]
for value in rgb_list:
if value > MAX_COLOR:
rgb_list[rgb_list.index(value)] = MAX_COLOR
if value < 0:
rgb_list[rgb_list.index(value)] = abs(value)
return (int(rgb_list[0]), int(rgb_list[1]),
int(rgb_list[2]))
def rgb_to_hex(self, color):
return "#%02x%02x%02x" % color
def mandel(self, x, y, z, iteration):
bmin = 100
bmax = 255
power_z = 2
mod_z = math.sqrt((z.real * z.real) + (z.imag * z.imag))
#If its not in the set or we have reached the maximum depth
if abs(z) >= float(power_z) or iteration == DEPTH:
z = z
if iteration > 255:
factor = (iteration / DEPTH) * 255
else:
factor = iteration
logs = math.log2(math.log2(abs(z) + 1 ) / math.log2(power_z))
r = g = math.floor(factor + 5 - logs)
b = bmin + (bmax - bmin) * r / 255
rgb = (abs(r), abs(g), abs(round(b)))
self.canvas.create_line(x, y, x + 1, y + 1,
fill = self.rgb_to_hex(rgb))
else:
z = (z * z) + self.c
self.mandel(x, y, z, iteration + 1)
return z
The difference between colors #000000, #010000, ..., #FE0000, #FF0000 is so small that you obtain a smooth gradient from black to red. Hence, simply round your values: Suppose your smoothened color values of your smoothness function range from 0 to (excl) 1, then you simply use
(int) (value * 256)
I am having some problems in matlab i don't understand. The following piece of code analyses a collection of images, and should return a coherent image (and always did).
But since I've put an if-condition in the second for-loop (for optimisation purposes) it returns an interlaced image.
I don't understand why, and am getting ready to throw my computer out the window. I suspect it has something to do with ind2sub, but as far as i can see everything is working just fine! Does anyone know why it's doing this?
function imageMedoid(imageList, resizeFolder, outputFolder, x, y)
% local variables
medoidImage = zeros([1, y*x, 3]);
alphaImage = zeros([y x]);
medoidContainer = zeros([y*x, length(imageList), 3]);
% loop through all images in the resizeFolder
for i=1:length(imageList)
% get filename and load image and alpha channel
fname = imageList(i).name;
[container, ~, alpha] = imread([resizeFolder fname]);
% convert alpha channel to zeros and ones, add to alphaImage
alphaImage = alphaImage + (double(alpha) / 255);
% add (r,g,b) values to medoidContainer and reshape to single line
medoidContainer(:, i, :) = reshape(im2double(container), [y*x 3]);
end
% loop through every pixel
for i=1:(y * x)
% convert i to coordinates for alphaImage
[xCoord, yCoord] = ind2sub([x y],i);
if alphaImage(yCoord, xCoord) == 0
% write default value to medoidImage if alpha is zero
medoidImage(1, i, 1:3) = 0;
else
% calculate distances between all values for current pixel
distances = pdist(squeeze(medoidContainer(i,:,1:3)));
% convert found distances to matrix of distances
distanceMatrix = squareform(distances);
% find index of image with the medoid value
[~, j] = min(mean(distanceMatrix,2));
% write found medoid value to medoidImage
medoidImage(1, i, 1:3) = medoidContainer(i, j, 1:3);
end
end
% replace values larger than one (in alpha channel) by one
alphaImage(alphaImage > 1) = 1;
% reshape image to original proportions
medoidImage = reshape(medoidImage, y, x, 3);
% save medoid image
imwrite(medoidImage, [outputFolder 'medoid_modified.png'], 'Alpha', alphaImage);
end
I didn't include the whole code, just this function (for brevity's sake), if anyone needs more (for a better understanding of it), please let me know and i'll include it.
When you call ind2sub, you give the size [x y], but the actual size of alphaImage is [y x] so you are not indexing the correct location with xCoord and yCoord.