Annoying bug in MATLAB - image

I'm doing a project and i needed to write two functions. The first is mk_key and its job is to convert a 24 RGB image into a vector consisting of consecutive bytes followed by a trailer consisting of 4 bytes that hold the dimensions of the image for reconstructing (so the vector size is [1 m*n*3+4]) then the elements of that vector are shuffled randomly according to a seed value (acting like a password) and finally the vector is saved to a file using hexa bytes; the other function is use_key and it is used to reconstruct the image from the key file, this is done by re-arranging the vector elements back into their positions and then using the data at the trailer to reconstruct the full RGB image. The only problem i have is that the reconstructed image has most of it's pixels lost and it only shows about 1/6 of the image and only at the red plane, the bytes at the other planes appear gray.
mk_key:
function mk_key(img, dest_file, seed)
s=size(img);
m=s(1);
n=s(2);
rg = zeros([1 m*n 3],'uint8');
for i=drange(1:m)
for j=drange(1:n)
rg(1,n*i+j-n,:)=img(i,j,:); %convert rectangular image matrix into row image
end
end
rgf = zeros([1 (m*n*3)+4],'uint8');
for x=drange(1:3)
rgf(1,(m*n*(x-1))+1:m*n*x)=rg(1,1:m*n,x);
end
mm=uint16(m);
nn=uint16(n);
rgf(1,(m*n*3)+1)=uint8(bitand(mm,hex2dec('00ff')));
rgf(1,(m*n*3)+2)=uint8(bitshift(bitand(mm,hex2dec('ff00')),-8));
rgf(1,(m*n*3)+3)=uint8(bitand(nn,hex2dec('00ff')));
rgf(1,(m*n*3)+4)=uint8(bitshift(bitand(nn,hex2dec('ff00')),-8));
rng(seed);
idxs = randperm(((m*n*3)+4)); % generate a random sequence representing byte locations
sg = zeros([1 ((m*n*3)+4)],'uint8');
for i=drange(1:((m*n*3)+4))
sg(1,i)=rgf(1,idxs(i));
end
f = fopen(dest_file, 'w');
for i=drange(1:((m*n*3)+4))
fprintf(f, '%x %x', sg(1,i));
end
fclose('all');
end
use_key:
function [img]=use_key(source_file, seed)
key_file=fopen(source_file);
key=fscanf(key_file,'%x %x');
key=key'; %Transpose column vector into row vector
key=uint8(key);
s=size(key);
rng(seed);
idxs = randperm(s(2)); % generate a random sequence representing byte locations
mgf = zeros([1 s(2)],'uint8');
for i=drange(1:s(2))
mgf(1,idxs(i))=key(1,i);
end
m=uint16(mgf(1,s(2)-3))+uint16(mgf(1,s(2)-2))*(16^2);
n=uint16(mgf(1,s(2)-1))+uint16(mgf(1,s(2)))*(16^2);
img = zeros([m n 3],'uint8');
for x=drange(1:3)
for i=drange(1:m)
for j=drange(1:n)
img(i,j,x)=mgf(1,(n*i+j-n)+(m*n)*(x-1));%convert row matrix into rectangular image matrix
end
end
end
fclose('all');
end

Whatever the bug is, it's somewhere in those horrible nested loops. Rather than attempt to fix what looks like a direct port of C code, I started cleaning up all the confusing and needlessly overcomplicated bits so I could make sense of it; by the time I'd finished, there wasn't much left:
function mk_key(img, dest_file, seed)
s = uint16(size(img));
s = typecast(s(1:2), 'uint8');
rg = reshape(img, 1, []);
rgf = [rg s];
rng(seed);
idxs = randperm(numel(rgf));
sg = rgf(idxs);
f = fopen(dest_file, 'w');
fprintf(f, '%x ', sg);
fclose(f);
end
and correspondingly:
function [img] = use_key(source_file, seed)
key_file = fopen(source_file);
key = fscanf(key_file,'%x');
fclose(key_file);
rng(seed);
idxs = randperm(numel(key));
mgf = zeros(1, numel(key), 'uint8');
mgf(idxs) = key;
s = typecast(mgf(end-3:end), 'uint16');
img = reshape(mgf(1:end-4), s(1), s(2), 3);
end
Whilst the ordering from reshape is different compared to your loops, that makes no practical difference given that the vector then gets shuffled - it's more robust, works as expected and is considerably quicker.

Related

Image Processing: Algorithm taking too long in MATLAB

I am working in MATLAB to process two 512x512 images, the domain image and the range image. What I am trying to accomplish is the following:
Divide both domain and range images into 8x8 pixel blocks
For each 8x8 block in the domain image, I have to apply a linear transformations to it and compare each of the 4096 transformed blocks with each of the 4096 range blocks.
Compute error in each case between the transformed block and the range image block and find the minimum error.
Finally I'll have for each 8x8 range block, the id of the 8x8 domain block for which the error was minimum (error between the range block and the transformed domain block)
To achieve this, I have written the following code:
RangeImagecolor = imread('input.png'); %input is 512x512
DomainImagecolor = imread('input.png'); %Range and Domain images are identical
RangeImagetemp = rgb2gray(RangeImagecolor);
DomainImagetemp = rgb2gray(DomainImagecolor);
RangeImage = im2double(RangeImagetemp);
DomainImage = im2double(DomainImagetemp);
%For the (k,l)th 8x8 range image block
for k = 1:64
for l = 1:64
minerror = 9999;
min_i = 0;
min_j = 0;
for i = 1:64
for j = 1:64
%here I compute for the (i,j)th domain block, the transformed domain block stored in D_trans
error = 0;
D_trans = zeros(8,8);
R = zeros(8,8); %Contains the pixel values of the (k,l)th range block
for m = 1:8
for n = 1:8
R(m,n) = RangeImage(8*k-8+m,8*l-8+n);
%ApplyTransformation can depend on (k,l) so I can't compute the transformation outside the k,l loop.
[m_dash,n_dash] = ApplyTransformation(8*i-8+m,8*j-8+n);
D_trans(m,n) = DomainImage(m_dash,n_dash);
error = error + (R(m,n)-D_trans(m,n))^2;
end
end
if(error < minerror)
minerror = error;
min_i = i;
min_j = j;
end
end
end
end
end
As an example ApplyTransformation, one can use the identity transformation:
function [x_dash,y_dash] = Iden(x,y)
x_dash = x;
y_dash = y;
end
Now the problem I am facing is the high computation time. The order of computation in the above code is 64^5, which is of the order 10^9. This computation should take at the worst minutes or an hour. It takes about 40 minutes to compute just 50 iterations. I don't know why the code is running so slow.
Thanks for reading my question.
You can use im2col* to convert the image to column format so each block forms a column of a [64 * 4096] matrix. Then apply transformation to each column and use bsxfun to vectorize computation of error.
DomainImage=rand(512);
RangeImage=rand(512);
DomainImage_col = im2col(DomainImage,[8 8],'distinct');
R = im2col(RangeImage,[8 8],'distinct');
[x y]=ndgrid(1:8);
function [x_dash, y_dash] = ApplyTransformation(x,y)
x_dash = x;
y_dash = y;
end
[x_dash, y_dash] = ApplyTransformation(x,y);
idx = sub2ind([8 8],x_dash, y_dash);
D_trans = DomainImage_col(idx,:); %transformation is reduced to matrix indexing
Error = 0;
for mn = 1:64
Error = Error + bsxfun(#minus,R(mn,:),D_trans(mn,:).').^2;
end
[minerror ,min_ij]= min(Error,[],2); % linear index of minimum of each block;
[min_i min_j]=ind2sub([64 64],min_ij); % convert linear index to subscript
Explanation:
Our goal is to reduce number of loops as much as possible. For it we should avoid matrix indexing and instead we should use vectorization. Nested loops should be converted to one loop. As the first step we can create a more optimized loop as here:
min_ij = zeros(4096,1);
for kl = 1:4096 %%% => 1:size(D_trans,2)
minerror = 9999;
min_ij(kl) = 0;
for ij = 1:4096 %%% => 1:size(R,2)
Error = 0;
for mn = 1:64
Error = Error + (R(mn,kl) - D_trans(mn,ij)).^2;
end
if(Error < minerror)
minerror = Error;
min_ij(kl) = ij;
end
end
end
We can re-arrange the loops and we can make the most inner loop as the outer loop and separate computation of the minimum from the computation of the error.
% Computation of the error
Error = zeros(4096,4096);
for mn = 1:64
for kl = 1:4096
for ij = 1:4096
Error(kl,ij) = Error(kl,ij) + (R(mn,kl) - D_trans(mn,ij)).^2;
end
end
end
% Computation of the min
min_ij = zeros(4096,1);
for kl = 1:4096
minerror = 9999;
min_ij(kl) = 0;
for ij = 1:4096
if(Error(kl,ij) < minerror)
minerror = Error(kl,ij);
min_ij(kl) = ij;
end
end
end
Now the code is arranged in a way that can best be vectorized:
Error = 0;
for mn = 1:64
Error = Error + bsxfun(#minus,R(mn,:),D_trans(mn,:).').^2;
end
[minerror ,min_ij] = min(Error, [], 2);
[min_i ,min_j] = ind2sub([64 64], min_ij);
*If you don't have the Image Processing Toolbox a more efficient implementation of im2col can be found here.
*The whole computation takes less than a minute.
First things first - your code doesn't do anything. But you likely do something with this minimum error stuff and only forgot to paste this here, or still need to code that bit. Never mind for now.
One big issue with your code is that you calculate transformation for 64x64 blocks of resulting image AND source image. 64^5 iterations of a complex operation are bound to be slow. Rather, you should calculate all transformations at once and save them.
allTransMats = cell(64);
for i = 1 : 64
for j = 1 : 64
allTransMats{i,j} = getTransformation(DomainImage, i, j)
end
end
function D_trans = getTransformation(DomainImage, i,j)
D_trans = zeros(8);
for m = 1 : 8
for n = 1 : 8
[m_dash,n_dash] = ApplyTransformation(8*i-8+m,8*j-8+n);
D_trans(m,n) = DomainImage(m_dash,n_dash);
end
end
end
This serves to get allTransMat and is OUTSIDE the k, l loop. Preferably as a simple function.
Now, you make your big k, l, i, j loop, where you compare all the elements as needed. Comparison could be also done block-wise instead of filling a small 8x8 matrix, yet doing it per element for some reason.
m = 1 : 8;
n = m;
for ...
R = RangeImage(...); % This will give 8x8 output as n and m are vectors.
D = allTransMats{i,j};
difference = sum(sum((R-D).^2));
if (difference < minDifference) ...
end
Even though this is a simple no transformations case, this speeds up code a lot.
Finally, are you sure you need to compare each block of transformed output with each block in the source? Typically you compare block1(a,b) with block2(a,b) - blocks (or pixels) on the same position.
EDIT: allTransMats requires k and l too. Ouch. There is NO WAY to make this fast for a single iteration, as you require 64^5 calls to ApplyTransformation (or a vectorization of that function, but even then it might not be fast - we would have to see the function to help here).
Therefore, I will re-iterate my advice to generate all transformations and then perform lookup: this upper part of the answer with allTransMats generation should be changed to have all 4 loops and generate allTransMats{i,j,k,l};. It WILL be slow, there is no way around that as I mentioned in the upper part of edit. But, it is a cost you pay once, as after saving the allTransMats, all further image analyses will be able to simply load it instead of generating it again.
But ... what do you even do? Transformation that depends on source and destination block indices plus pixel indices (= 6 values total) sounds like a mistake somewhere, or a prime candidate to optimize instead of all the rest.

How to divide any sized image to equal sized block using only for loop in matlab?

How to divide image to equal sized blocks using simple matlab for loop only?
As a beginner I have tried but its showing error.I have done same thing using mat2cell and using simple calculation.
This is my code:
[rows, columns, numberOfColorChannels] = size(p);
r4 = int32(rows/4);
c4 = int32(columns/4);
% Extract images.
image1 = p(1:r4, 1:c4);
image2 = p(1:r4, c4+1:2*c4);
image3 = p(1:r4, 2*c4+1:3*c4);
image4 = p(1:r4, 3*c4+1:4*c4);
I need to do it with a for loop only.
First things first if you separate x and y into 4 equally sized sections you will get 16 smaller images. You need to understand this first part.
[rows, columns, numberOfColorChannels] = size(p);
r4 = int32(rows/4);
c4 = int32(columns/4);
output = zeros(16,r4,c4,numberOfColorChannels);
cnt = 1;
for i=1:4,
for j=1:4,
output(cnt,:,:,:) = p((i-1)*r4+1:i*r4, (j-1)*c4+1:j*c4);
cnt = cnt + 1;
end
end
The code basically does what you've already done but in two dimensions.

Matlab histogram equilization without using built in function

Hello I am new at Matlab..I am trying to do histogram equilzation without using histeq...
but for some reason i always get an error of : ??? Index exceeds matrix dimensions.
here is my code..................................................Thanks for your help
clc
I = imread ('Machine-Edge.PNG');
I2 = rgb2gray(I);
colormap gray;
y = imhist(I2);
%using hist eq. built in fn
I3= histeq(I2);
z= imhist(I3);
%my equalization
r = size(I2,1);
c = size(I2,2);
A= zeros(1,256);
%counting number of pixels of the image and putting the count in Array A
for j=1:r
for x=1:c
v=I2(j,x);
A(v+1)=A(v+1)+1;
end
end
%pi=n/size
for y=1;256
pi(y)= ((A(y))/(r*c));
end
%calculate CI (cumulated pi )
ci(1)=pi(1);
for yy=2;256
ci(yy) = ci(yy-1)+ pi(yy);
end
%calculate T=range *Ci
for b=1;256
T(b)=ci(b)*255;
end
%equilization..replacing each pixel with T value
for j=1:r
for x=1:c
I4(j,x) =T(I2(j,x));
end
end
vv= imhist(I4);
figure
subplot(3,2,1)
imagesc(I2)
subplot(3,2,2)
plot(y)
subplot(3,2,3)
imagesc(I3)
subplot(3,2,4)
plot(z)
subplot(3,2,5)
imagesc(I4)
subplot(3,2,6)
plot(vv)
This is an old post but the OP used ; instead of : in their for loops (i.e. for y=1;256 should read for y=1:256). the corrected code is below:
clc
I = imread ('Machine-Edge.PNG');
I2 = rgb2gray(I);
colormap gray;
y = imhist(I2);
%using hist eq. built in fn
I3= histeq(I2);
z= imhist(I3);
%my equalization
r = size(I2,1);
c = size(I2,2);
A= zeros(1,256);
%counting number of pixels of the image and putting the count in Array A
for j=1:r
for x=1:c
v=I2(j,x);
A(v+1)=A(v+1)+1;
end
end
%pi=n/size
for y=1:256
pi(y)= ((A(y))/(r*c));
end
%calculate CI (cumulated pi )
ci(1)=pi(1);
for yy=2:256
ci(yy) = ci(yy-1)+ pi(yy);
end
%calculate T=range *Ci
for b=1:256
T(b)=ci(b)*255;
end
%equilization..replacing each pixel with T value
for j=1:r
for x=1:c
I4(j,x) =T(I2(j,x));
end
end
vv= imhist(I4);
figure
subplot(3,2,1)
imagesc(I2)
subplot(3,2,2)
plot(y)
subplot(3,2,3)
imagesc(I3)
subplot(3,2,4)
plot(z)
subplot(3,2,5)
imagesc(I4)
subplot(3,2,6)
plot(vv)

Image manipulation with Matlab - intensity and tif image

I have to analyse a set of images, and these are the operations I need to perform:
sum another set of images (called open beam in the code), calculate the median and rotate it by 90 degrees;
load a set of images, listed in the file "list.txt";
the images have been collected in groups of 3. For each group, I want to produce an image whose intensity values are 3 times the image median above a certain threshold and otherwise equal to the sum of the intensity values;
for each group of three images, subtract the open beam median (calculated in 1.) from the combined image (calculated in 3.)
Considering one of the tifs produced using the process above, I have that the maximum value is 65211, which is not 3* the median for the three images of the corresponding group (I checked considering the pixel position). Do you have any suggestion on why this happens, and how I could fix it?
The code is reported below. Thanks!
%Here we calculate the average for the open beam
clear;
j = 0;
for i=1:5
s = sprintf('/Users/Alberto/Desktop/Midi/17_OB_2.75/midi_%04i.fits',i);
j = j+1;
A(j,:,:) = uint16(fitsread(s));
end
OB_median = median(A,1);
OB_median = squeeze(OB_median);
OB_median_rot=rot90(OB_median);
%Here we calculate, for each projection, the average value from the three datasets
%Read list of images from text file
fid = fopen('/Users/Alberto/Desktop/Midi/list.txt', 'r');
a = textscan(fid, '%s');
fclose(fid);
%load images
j = 0;
for i = 1:1:42 %556 entries; 543 valid values
s = sprintf('/Users/Alberto/Desktop/Midi/%s',a{1,1}{i,1});
j = j+1;
A(j,:,:) = uint16(fitsread(s));
end
threshold = 80 %This is a discretional number. I put it after noticing
%that we get the same number of pixels with a value >100 if we use 80 or 50.
k = 0;
for ii = 1:3:42
N(1,:,:) = A(ii,:,:);
N(2,:,:) = A(ii+1,:,:);
N(3,:,:) = A(ii+2,:,:);
median_N = median(N,1);
median_N = squeeze(median_N);
B(:,:) = zeros(2160,2592);
for i = 1:1:2160
for j = 1:1:2592
RMS(i,j) = sqrt((double(N(1,i,j).^2) + double(N(2,i,j).^2) + double(N(3,i,j).^2))/3);
if RMS(i,j) > threshold
%B(i,j) = 30;
B(i,j) = 3*median_N(i,j);
else
B(i,j) = A(ii,i,j) + A(ii+1,i,j) + A(ii+2,i,j);
%B(i,j) = A(ii,i,j);
end
end
end
k = k+1;
filename = sprintf('/Users/Alberto/Desktop/Midi/Edited_images/Despeckled_images/despeckled_image_%03i.tif',k);
%Now we rotate the matrix
B_rot=rot90(B);
imwrite(B_rot, filename);
%imwrite(uint16(B_rot), filename);
%Now we subtract the OB median
B_final_rot = double(B_rot) - 3*double(OB_median_rot);
filename = sprintf('/Users/Alberto/Desktop/Midi/Edited_images/Final_image/final_image_%03i.tif',k);
imwrite(uint16(B_final_rot), filename);
end
The maximum integer that can be represented by the uint16 data type is
>> a=100000; uint16(a)
ans =
65535
To circumvent this limitation you need to rescale your data as type double and adjust the range (the image contrast) to agree with the limits imposed by the uint16 data type, before saving as uint16.

How to make a Gaussian filter in Matlab

I have tried to make a Gaussian filter in Matlab without using imfilter() and fspecial().
I have tried this but result is not like the one I have with imfilter and fspecial.
Here is my codes.
function Gaussian_filtered = Gauss(image_x, sigma)
% for single axis
% http://en.wikipedia.org/wiki/Gaussian_filter
Gaussian_filtered = exp(-image_x^2/(2*sigma^2)) / (sigma*sqrt(2*pi));
end
for 2D Gaussian,
function h = Gaussian2D(hsize, sigma)
n1 = hsize;
n2 = hsize;
for i = 1 : n2
for j = 1 : n1
% size is 10;
% -5<center<5 area is covered.
c = [j-(n1+1)/2 i-(n2+1)/2]';
% A product of both axes is 2D Gaussian filtering
h(i,j) = Gauss(c(1), sigma)*Gauss(c(2), sigma);
end
end
end
and the final one is
function Filtered = GaussianFilter(ImageData, hsize, sigma)
%Get the result of Gaussian
filter_ = Gaussian2D(hsize, sigma);
%check image
[r, c] = size(ImageData);
Filtered = zeros(r, c);
for i=1:r
for j=1:c
for k=1:hsize
for m=1:hsize
Filtered = Filtered + ImageData(i,j).*filter_(k,m);
end
end
end
end
end
But the processed image is almost same as the input image. I wonder the last function GaussianFiltered() is problematic...
Thanks.
here's an alternative:
Create the 2D-Gaussian:
function f=gaussian2d(N,sigma)
% N is grid size, sigma speaks for itself
[x y]=meshgrid(round(-N/2):round(N/2), round(-N/2):round(N/2));
f=exp(-x.^2/(2*sigma^2)-y.^2/(2*sigma^2));
f=f./sum(f(:));
Filtered image, given your image is called Im:
filtered_signal=conv2(Im,gaussian2d(N,sig),'same');
Here's some plots:
imagesc(gaussian2d(7,2.5))
Im=rand(100);subplot(1,2,1);imagesc(Im)
subplot(1,2,2);imagesc(conv2(Im,gaussian2d(7,2.5),'same'));
This example code is slow because of the for-loops. In matlab you can better use conv2, as suggested by user:bla, or just use filter2.
I = imread('peppers.png'); %load example data
I = I(:,:,1);
N=5; %must be odd
sigma=1;
figure(1);imagesc(I);colormap gray
x=1:N;
X=exp(-(x-((N+1)/2)).^2/(2*sigma^2));
h=X'*X;
h=h./sum(h(:));
%I=filter2(h,I); %this is faster
[is,js]=size(I);
Ib = NaN(is+N-1,js+N-1); %add borders
b=(N-1)/2 +1;
Ib(b:b+is-1,b:b+js-1)=I;
I=zeros(size(I));
for i = 1:is
for j = 1:js
I(i,j)=sum(sum(Ib(i:i+N-1,j:j+N-1).*h,'omitnan'));
end
end
figure(2);imagesc(I);colormap gray

Resources