imcrop into 1 variable - image

I have made a a group of new pictures using imcrop from the same file, With this code, I know it's long but since the distances are not always the same I find no other way to do it than this:
A001=imcrop(A,[65 159 95 332]);
A002=imcrop(A,[182 161 95 332]);
A003=imcrop(A,[297 164 95 332]);
A004=imcrop(A,[402 165 90 332]);
A005=imcrop(A,[495 168 90 332]);
A006=imcrop(A,[606 166 90 332]);
A007=imcrop(A,[705 171 90 332]);
A008=imcrop(A,[808 175 90 332]);
A009=imcrop(A,[922 175 90 332]);
A0010=imcrop(A,[1031 175 90 332]);
Then I have a series of tasks to be performed on each of the new images, how do i get around that the easiest way? When I import multiple jpegs from a folder I can get it to make a dataset of the files but when I try to do the same with A001:A0010 I get nothing.
This is the task that I want to perform:
greenChannel = A(:, :, 2);
BW = edge(greenChannel,'Prewitt');
figure, imshow(BW)
%Dialate Lines
se90 = strel('line', 3, 90);
se0 = strel('line', 3, 0);
BWsdil = imdilate(BW, [se90 se0]);;
figure, imshow(BWsdil), title('dilated gradient mask');
%Fill Lines
BWdfill = imfill(BWsdil, 'holes');
figure, imshow(BWdfill);
title('binary image with filled holes');
BWnobord = imclearborder(BWdfill, 4);
figure, imshow(BWnobord), title('cleared border image');
seD = strel('diamond',1);
BWfinal = imerode(BWnobord,seD);
BWfinal = imerode(BWfinal,seD);
figure, imshow(BWfinal), title('segmented image');
L = bwlabel(BWfinal);
s = regionprops(L,'centroid');
What I need help to do is somehow get A001:A0010 into A in the top and run that sequence of commands, hope someone can help me achieve that!

This is hairy, but here goes:
A = imread('peppers.png');
A = imresize(A, [1500 1500]); % to handle the indexing range.
A001=imcrop(A,[65 159 95 332]);
A002=imcrop(A,[182 161 95 332]);
A003=imcrop(A,[297 164 95 332]);
A004=imcrop(A,[402 165 90 332]);
A005=imcrop(A,[495 168 90 332]);
A006=imcrop(A,[606 166 90 332]);
A007=imcrop(A,[705 171 90 332]);
A008=imcrop(A,[808 175 90 332]);
A009=imcrop(A,[922 175 90 332]);
A0010=imcrop(A,[1031 175 90 332]);
w = who; % returns the names of all your current variables in a cell.
for i = 1:numel(w)
% A00 is unique to all the variables you want to process.
if ~isempty(strfind(w{i}, 'A00'))
% hard coding greenChannel and extracting the second plane.
eval(['greenChannel = ',w{i},'(:,:,2)']);
% do the rest of the processing here,
% from BW = edge ... to regionprops.
% You may have to save the s structure as a cell array.
end
end
This uses the who command to extract all the current variables, and the eval command to evaluate what is passed in as text, based on the variable names. Note that using eval is dangerous, and should be done only if there are no better alternatives. See Use and implications of eval('expression') in MATLAB code?

Related

How to down sample image array, without changing pixel values

I have image segmentation project, and ground truth labels given as images where pixel value stands for the label. I need to resize the images and labels, while keeping the labels in the same value set.
I tried many things, All change the value set.
Lets create dummy data
from skimage.transform import rescale, resize
from scipy import ndimage
from PIL import Image
import cv2
mask = np.zeros((30,20), dtype=np.uint16)
mask[22:26,12:30]=70
mask[25:27,14:17]=30
print('original label', mask.shape, np.unique(mask))
Outputs: original label shape: (30, 20) original label values: [ 0 30 70]
I need to resize label, so the result will have only 0, 30, 70 values.
What I tried
skimage_resized = resize(mask, (mask.shape[0]//2, mask.shape[1]//2), mode='constant')
print(skimage_resized.shape, np.unique(mask_resized))
skimage_rescale = rescale(mask, 1.0/2.0, mode='constant')
print(skimage_rescale.shape, np.unique(mask_resized))
ndimage_resized = ndimage.interpolation.zoom(mask, 0.5)
print(ndimage_resized.shape, np.unique(mask_resized))
cv2_resized = cv2.resize(mask, (mask.shape[0]//2, mask.shape[1]//2),
interpolation=cv2.INTER_NEAREST)
print(cv2_resized.shape, np.unique(mask_resized))
mask_pil = Image.fromarray(mask, mode=None)
pil_resized = mask_pil.thumbnail((mask.shape[0]//2, mask.shape[1]//2), Image.NEAREST)
print(skimage_resized.shape, np.unique(pil_resized))
Output:
(15, 10) [ 0 5 6 28 29 30 31 61 62 65 70 71 74 75 76]
(15, 10) [ 0 5 6 28 29 30 31 61 62 65 70 71 74 75 76]
(15, 10) [ 0 5 6 28 29 30 31 61 62 65 70 71 74 75 76]
(10, 15) [ 0 5 6 28 29 30 31 61 62 65 70 71 74 75 76]
(15, 10) [None]
Found a solution with openCV.
import numpy as np
import cv2
resizeto = 2
small_lable = cv2.resize(mask, (mask.shape[1]//resizeto,
mask.shape[0]//resizeto),
interpolation=cv2.INTER_NEAREST)
small_lable = (np.array(small_lable)).astype('uint8')
print(small_lable.shape, np.unique(small_lable))
plt.imshow(small_lable)
output:
(15, 10) [ 0 30 70]
From the docs (emphasis mine):
Note that when down-sampling an image, resize and rescale should perform Gaussian smoothing to avoid aliasing artifacts. See the anti_aliasing and anti_aliasing_sigma arguments to these functions.
Downscale serves the purpose of down-sampling an n-dimensional image by integer factors using the local mean on the elements of each block of the size factors given as a parameter to the function.
One possible workaround to your problem would be downsampling through basic slicing:
import numpy as np
dx, dy = 2, 2
mask = np.zeros((30, 20), dtype=np.uint16)
mask[22:26, 12:30] = 70
mask[25:27, 14:17] = 30
downsampled = mask[::dx, ::dy]
print(downsampled.shape, np.unique(downsampled))
The output from the snippet above is:
(15, 10) [ 0 30 70]

Outputting Regionprops to csv in MATLAB

I'm currently running some data analysis on a lot of pictures and the code i have running is the following:
close all
clear all
clc
A=imread('Ring_1_frame_120.jpg'); %Load picture
%A01-A010 = xmin ymin width height
%for all vials
A001=imcrop(A,[65 159 95 332]);
A002=imcrop(A,[182 161 95 332]);
A003=imcrop(A,[297 164 95 332]);
A004=imcrop(A,[402 165 90 332]);
A005=imcrop(A,[495 168 90 332]);
A006=imcrop(A,[606 166 90 332]);
A007=imcrop(A,[705 171 90 332]);
A008=imcrop(A,[808 175 90 332]);
A009=imcrop(A,[922 175 90 332]);
A0010=imcrop(A,[1031 175 90 332]);
w = who; % returns the names of all your current variables in a cell.
for i = 1:numel(w)
% A00 is unique to all the variables you want to process.
if ~isempty(strfind(w{i}, 'A00'))
% hard coding greenChannel and extracting the second plane.
eval(['greenChannel = ',w{i},'(:,:,2)']);
BW = edge(greenChannel,'Prewitt');
%figure, imshow(BW);
%Dialate Lines
se90 = strel('line', 3, 90);
se0 = strel('line', 3, 0);
BWsdil = imdilate(BW, [se90 se0]);
%figure, imshow(BWsdil), title('dilated gradient mask');
%Fill Lines
BWdfill = imfill(BWsdil, 'holes');
%figure, imshow(BWdfill), title('binary image with filled holes');
%Clean up borders
BWnobord = imclearborder(BWdfill, 4);
%figure, imshow(BWnobord), title('cleared border image');
%Final cleanup
seD = strel('diamond',1);
BWfinal = imerode(BWnobord,seD);
BWfinal = imerode(BWfinal,seD);
figure, imshow(BWfinal), title('segmented image');
L = bwlabel(BWfinal);
s = regionprops(L,'centroid');
data(:,:,i) = s; %save the xy coords as data matrix
end
end
The goal I'm trying to achieve is getting the variable s into a csv file, but I'm stuck at the last line since it's not working. It keeps overwriting itself. s is a structure ranging from 3x1 to 5x1 and I have also tried to use struct2cell and mat2cell but that was unsuccessful.
s is a structure, so what you need to do is unpack the structure so that it becomes a matrix, then you can save the matrix to file. s contains a field called Centroid, so you need to access that field.
However before I address that point, checking to see how many variables are in your workspace so you can determine how many times your loop has to iterate.... is very bad practice. Especially if you are using each variable name as a separate occurrence for processing. I highly recommend you use a structure to encapsulate this or some sort of cell array.
If I can provide a canonical post, please consult user Adriaan's excellent post on how to avoid dynamic variable names and sheds light on what I'm about to talk about here.
Something like this would work instead. I'll use a cell array because (at least to me) it is easier. Place your desired coordinates in a 2D matrix where each row is the top-left corner of the location in the image you want to process as well as the width and height (basically suitable for imcrop), then loop over each set of coordinates and place the cropped image as an element in a cell array. Cell array use is important because the dimensions per cropped image are different and so you can't use a normal matrix here:
A=imread('Ring_1_frame_120.jpg'); %Load picture
%A01-A010 = xmin ymin width height
coords = [65 159 95 332; 182 161 95 332; 297 164 95 332; 402 165 90 332;...
495 168 90 332; 606 166 90 332; 705 171 90 332; 808 175 90 332;...
922 175 90 332; 1031 175 90 332];
numImages = size(coords,1);
images = cell(1,numImages);
for ii = 1 : numImages
images{ii} = imcrop(A,coords(ii,:));
end
images is now a cell array of cropped images that belong to the image A. To access the right image, you can use images to do that like so:
img = images{ii};
ii is the image number you wish to access. Another comment I'd like to make is your use of eval. It is really not recommended in your loop either... which is why I decided to change the logic.
Do this instead:
for ii = 1 : numImages
% hard coding greenChannel and extracting the second plane.
greenChannel = images{ii}(:,:,2); %// Change for green channel
%// Now code is the same as before
BW = edge(greenChannel,'Prewitt');
%figure, imshow(BW);
%Dilate Lines
se90 = strel('line', 3, 90);
se0 = strel('line', 3, 0);
BWsdil = imdilate(BW, [se90 se0]);
%figure, imshow(BWsdil), title('dilated gradient mask');
%Fill Lines
BWdfill = imfill(BWsdil, 'holes');
%figure, imshow(BWdfill), title('binary image with filled holes');
%Clean up borders
Wnobord = imclearborder(BWdfill, 4);
%figure, imshow(BWnobord), title('cleared border image');
%Final cleanup
seD = strel('diamond',1);
BWfinal = imerode(BWnobord,seD);
BWfinal = imerode(BWfinal,seD);
figure, imshow(BWfinal), title('segmented image');
...
end
Alright, so now how do we get the coordinates of the centroid and save them to file? You simply need to unpack the structure and get the centroid coordinates. Make sure data is declared at the top is now a cell array:
data = cell(1, numImages);
The reason why you need a cell array (again) is because you don't know how many segmented components there are per cropped image you're looking at. Now finally at the end of your loop:
for ii = 1 : numImages
%// Your code...
%//...
L = bwlabel(BWfinal);
s = regionprops(L,'centroid');
%// New code
data{ii} = reshape([s.Centroid],2,[]).';
end
Now that you have the centroid coordinates stored in a cell array per cropped image, you can either create multiple CSVs where each CSV contains the centroids of each detected object for each cropped image, or you can concatenate all of the centroids together in a single matrix.
So, do either:
for ii = 1 : numImages
csvwrite(sprintf('data%d.csv', ii), data{ii});
end
... or
out = cat(1, data{:});
csvwrite('data.csv', out);
I'm not sure which method you want to use to write to file, but either of those should work.
You need to access struct elements using s(i).Centroid, as a minimal example,
a =imread('circlesBrightDark.png');
bw = a < 100;
s = regionprops(bw,'centroid');
for i =1:size(s)
data(:,:,i) = s(i).Centroid
end

MATLAB: finding values in one array and using those locations to change values in one channel of a separate 3 channel array

So I have a labeled array (array1) with connected regions of interest (background is all zeros, connected regions are all 1's for the first region, all 2's for the second, 3's for the 3rd etc.) I also have a vector (vector1) of the region labels I find important (ex. 1,6,9). I want to find the locations of these values in the labeled array and then change values in one channel of a separate 3 channel array at the same locations (want to color certain parts of an image green based regions of interest found in another image).
I can use the below code to change all channels, but don't know how to specify (img(y,x,1=0), img(y,x,2=0), img(y,x,3=255)).
for i=1:1:length(vector1)
img(array1==vector1(i))=255;
end
If I understand you correctly, you have a label map of objects - each with an associated ID with 0 as the background. You also have a vector of important IDs and a colour image that is associated with this label map.
You wish to set all locations that have an important ID to 1 colour. I would first create a logical mask where true means that the pixel is important and false otherwise. What I mean by important is that the pixel is either 1, 6 or 9 if we go with your example. You can use bsxfun combined with any to create this mask, then we can use this to index into your image and set the right colour to these locations.
Therefore, do this:
%// Create your logical mask
mask3D = bsxfun(#eq, array1, permute(vector(:), [3 2 1]));
mask = any(mask3D, 3);
%// Set the image pixels to blue at these locations
red = img(:,:,1);
green = img(:,:,2);
blue = img(:,:,3);
red(mask) = 0;
green(mask) = 0;
blue(mask) = 255;
img = cat(3, red, green, blue);
Here's a quick example run. Suppose we have this image with squares:
We can see that there are three squares. Let's change object 1 and object 3 to blue. Before we do that, we need to get a label map:
%// Originally a binary image
im = imread('http://i.stack.imgur.com/DnYQS.png');
%// Get label map
array1 = bwlabel(im);
%// Create colour version out of this binary image
img = 255*uint8(im);
img = cat(3, img, img, img);
array1 is our label map as you have also mentioned in your question and img is the colour version of the input image. Now, vector = [1 3] so we can change those objects. The labelling is such that the top left square is label 1, the middle is label 2 and the bottom right is label 3.
Once I do this and I run the above code, this is the image I get:
You can achieve it in two lines -
%// Create a 3D mask to select the labels [1,6,9] only, as listed in "vector1"
mask = bsxfun(#and,ismember(array1,vector1),ones(1,1,3));
%// Map "img" with "mask" and set them to tuple:[0,0,255] (blue color)
img(mask) = reshape(repmat([0 0 255],nnz(mask)/3,1),[],1)
Sample run -
array1 = %// Input
2 0 1 1 0 6
0 0 1 0 0 6
9 0 0 0 0 6
9 9 0 4 0 0
vector1 = %// Input
1 6 9
img(:,:,1) = %// Input: Before code run
228 19 175 30 192 188
204 23 34 164 149 248
188 204 185 84 189 222
14 241 29 167 60 22
img(:,:,2) =
94 202 197 59 200 136
95 94 53 164 26 24
175 53 100 124 75 104
153 23 141 39 61 27
img(:,:,3) =
29 246 111 48 197 172
201 111 168 68 101 110
75 178 28 204 70 116
154 194 239 125 10 156
img(:,:,1) = %// Output: After code run
228 19 0 0 192 0
204 23 0 164 149 0
0 204 185 84 189 0
0 0 29 167 60 22
img(:,:,2) =
94 202 0 0 200 0
95 94 0 164 26 0
0 53 100 124 75 0
0 0 141 39 61 27
img(:,:,3) =
29 246 255 255 197 255
201 111 255 68 101 255
255 178 28 204 70 255
255 255 239 125 10 156
Notice the positions of the values [1,6,9] in array1 and the corresponding value changes in img1 before and after code runs.

d3.js - Time scale with ticks in milliseconds

I need to plot a line graph where x-axis will have ticks representing time with milliseconds detail.
For x-scale, I am using d3.time.scale()
var xScale = d3.time.scale()
.range([0, width])
x-axis looks like:
var xAxis = d3.svg.axis()
.scale(xScale)
//.ticks(d3.time.second, 1)
.orient("bottom")
.tickFormat(d3.time.format("%H:%M %L"));
But values/ticks on x-axis are not generating as expected.
data for x-axis are date objects and they hold following values(sample data)
13:25:6 794 (%H:%M%S %L)
13:25:6 898
13:25:6 994
13:25:7 95
13:25:7 194
13:25:7 295
13:25:7 395
13:25:7 495
13:25:7 595
13:25:7 710
13:25:7 795
13:25:7 895
13:25:7 995
13:25:8 95
13:25:8 195
13:25:8 294
13:25:8 395
13:25:8 495
13:25:8 594
13:25:8 795
However if I take linear scale d3.scale.linear()
Ticks generated follow a expected series.
whats the correct way of using time scale with data having millisecond details.
How can I have tick intervals in seconds:milliseconds?
EDIT:
Also, how can I have ticks every few milliseconds say every 500 ms?
there is an API d3.time.second but nothing like d3.time.millisecond. How can I add one?
Fiddle using time scale
intervals in seconds:milliseconds means, you can try out this
.tickFormat(d3.time.format("%S %L"));
(Removed outdated previous answer)
I created an issue for this against the d3 project and it has been fixed: https://github.com/mbostock/d3/issues/1529
An example can be seen here: http://bl.ocks.org/mbostock/6618724

How to speed up matplotlib scatter ploting?

I need to draw a batch of scatter charts in matplotlib, and found the speed of matplotlib is slow, then I lineprofile the function, and found the hotspot is fig, ax = plt.subplots(), It costs 56.1% of time to creat a blank figure and axes !!
How to speed it up ? I mean, how can I reuse fig and ax to avoid creating them each time ?
Attach the profile report here (I cut some of the line to make it simple)
Total time: 0.733771 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
517 #profile
518 def df_scatter(df):
519 ''' draw the scatter plot for Pandas dataframe 'df'
533 '''
536
537 1 75 75.0 0.0 biggest_area = 1000
538 1 117 117.0 0.0 mycm = matplotlib.cm.get_cmap('jet') # 'spectral'
539
541 1 78 78.0 0.0 col_qty = len(df.columns)
543
544 1 1859 1859.0 0.1 x = list(df.ix[:,0].values)
545 1 1258 1258.0 0.0 y = list(df.ix[:,1].values)
551
552 1 1472345 1472345.0 56.1 fig, ax = plt.subplots()
556
557 1 7007 7007.0 0.3 plt.subplots_adjust(left=0.07, right=0.92, bottom=0.1, top=0.95)
558 1 179 179.0 0.0 x_margin, y_margin = (max(x)-min(x))/20, (max(y)-min(y))/20
563
564 1 71 71.0 0.0 if col_qty > 2:
565 1 1602 1602.0 0.1 r = list(df.ix[:,2].values)
566 1 309 309.0 0.0 size_r = np.array(biggest_area)*r/max(r)
585
586 1 34712 34712.0 1.3 sc = plt.scatter(x, y, marker='o', s=size_r, cmap=mycm, alpha=0.65)
587
588 # adding colorbar
589 1 542417 542417.0 20.7 cbaxes = fig.add_axes([0.94, 0.25, 0.02, 0.70])
590 1 165719 165719.0 6.3 cbar = plt.colorbar(sc, cax=cbaxes)
591 1 122 122.0 0.0 cbar.solids.set_edgecolor('face')
595
602 1 1061 1061.0 0.0 plt.figtext(0.94,0.10,"%0.1f"%(max(r)), fontproperties=TEXT_FONT_MEDIUM)
639 1 66 66.0 0.0 return fig
I think that the best way to do it is calling
fig = plt.figure()
ax=fig.add_subplot(111)
from outside of df_scatter. Then, pass it to df_scatter as arguments:
df_scatter(df,fig,ax):
or simply do inside df_scatter:
def df_scatter(df):
fig = plt.gcf()
ax = plt.gca()
after the creation of fig & axis was done.

Resources