strange result with JPEG compression - matlab

I want to implement the JPEG compression by using MATLAB. Well at the point where the symbols' probabilities (Huffman coding) are calculated i can see some NEGATIVE values. I am sure that this is not correct!!! if someone can give some help or directions i would really appreciate it. Thank all of you in advance. I use MATLAB R2012b. Here is the code:
clc;
clear all;
a = imread('test.png');
b = rgb2gray(a);
b = imresize(b, [256 256]);
b = double(b);
final = zeros(256, 256);
mask = [1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 0
1 1 1 1 1 0 0 0
1 1 1 1 0 0 0 0
1 1 1 0 0 0 0 0
1 1 0 0 0 0 0 0
1 0 0 0 0 0 0 0];
qv1 = [ 16 11 10 16 24 40 51 61
12 12 14 19 26 58 60 55
14 13 16 24 40 57 69 56
14 17 22 29 51 87 80 62
18 22 37 56 68 109 103 77
24 35 55 64 81 104 113 92
49 64 78 87 103 121 120 101
72 92 95 98 112 100 103 99];
t = dctmtx(8);
DCT2D = #(block_struct) t*block_struct.data*t';
msk = #(block_struct) mask.*block_struct.data;
for row = 1:8:256
for column = 1:8:256
x = (b(row:row+7, column:column+7));
xf = blockproc(x, [8 8], DCT2D);
xf1 = blockproc(xf, [8 8], msk);
xf1 = round(xf1./qv1).*qv1;
final(row:row+7, column:column+7) = xf1;
end
end
[symbols,p] = hist(final,unique(final));
bar(p, symbols);
p = p/sum(p); %NEGATIVE VALUES????

I think you might have the outputs of hist (symbols and p) swapped. The probability should be calculated from the bin counts, which is the first output of hist.
[nelements,centers] = hist(data,xvalues) returns an additional row vector, centers, indicating the location of each bin center on the x-axis. To plot the histogram, you can use bar(centers,nelements).
In other words, instead of your current line,
[symbols,p] = hist(final,unique(final));
just use,
[p,symbols] = hist(final,unique(final));
Also, final is a matrix rather than a vector, so nelements will be a matrix:
If data is a matrix, then a histogram is created separately for each column. Each histogram plot is displayed on the same figure with a different color.

Related

Matlab: find a value in a matrix

I have the following matrix:
A= [23 34 45 0 0 0; 21 34 0 0 23 11; 34 23 0 0 0 22]
I want to find if a value is present and if it's present, I want to find the following values.
Eg I want to find in A the value 23, if it's present I want like output a matrix only with 23 and its following values
B= [23 34 45 0 0 0; 0 0 0 0 23 11; 0 23 0 0 0 22]
This is an interesting question, and I have a non-loopy answer, it uses the interesting effect of cumsum and find to great efficiency.
G = zeros(size(A));
T = find(A==23);
G(T) = 1;
mask = cumsum(G,2)>0;
result = mask .* A;
>> result =
23 34 45 0 0 0
0 0 0 0 23 11
0 23 0 0 0 22
This is I think, one of the more efficient way of doing this.
========EDIT========
even better, use logical indexing:
B = A.*(cumsum(A==23,2)>0);
Thanks to #obchardon
find() returns the row and the column of the desired value, in your case "23", in matrix A.
using a for loop you can copy the value and its following ones:
A = [23 34 45 0 0 0; ...
21 34 0 0 23 11; ...
34 23 0 0 0 22];
[r, c] = find(A==23);
B = zeros(3,6);
for i=1:length(r)
columns = c(i):length(B);
B(i,columns) = A(r(i),columns);
end;

Change vector if even/odd in a matrix

I'm new to Matlab programming and I've only had 3 classes so far. I'm having problem with my homework. (Also I am from Iceland so english is not my first language, so please forgive my grammar)
I'm given a matrix, A and I'm supposed to change the value? of a vector to 0 if it is an even number and to 1 if it is an odd number.
This is what I have so far.
A = [90 100 87 43 20 58; 29 5 12 94 8 62; 75 21 36 83 35 24; 47 51 70 59 82 33];
B = zeros(size(A));
for k = 1:length(A)
if mod(A(k),2)== 0 %%number is even
B(k) = 0;
else
B(k) = 1; %%number is odd
end
end
B(A,2==0) = 0;
B(A,2~=0) = 1
What I am getting it this:
B =
0 0 0 0 0 0
1 1 0 0 0 0
1 0 0 0 0 0
1 0 0 0 0 0
1 0 0 0 0 0
If anyone could please help me, it would be greatly appreciated :)
You are very close. Don't use length(A) - use numel(A). length(A) returns the number of elements along the largest dimension. As such, because you have 6 columns and 4 rows, this loop will only iterate 6 times. numel returns the total number of elements in the array A, which is what you want as you want to iterate over each value in A.
Therefore:
A = [90 100 87 43 20 58; 29 5 12 94 8 62; 75 21 36 83 35 24; 47 51 70 59 82 33];
B = zeros(size(A));
for k = 1:numel(A) %// Change
if mod(A(k),2)== 0 %%number is even
B(k) = 0;
else
B(k) = 1; %%number is odd
end
end
The above loop will go through every single element in the matrix and set the corresponding element to 0 if even and 1 if odd.
However, I encourage you to use vectorized operations on your code. Don't use loops for this. Specifically, you can do this very easily with a single mod call:
B = mod(A,2);
mod(A,2) will compute the modulus of every value in the matrix A with 2 as the operand and output a matrix B of the same size. This will exactly compute the parity of each number.
We get for B:
>> A = [90 100 87 43 20 58; 29 5 12 94 8 62; 75 21 36 83 35 24; 47 51 70 59 82 33];
>> B = mod(A,2)
B =
0 0 1 1 0 0
1 1 0 0 0 0
1 1 0 1 1 0
1 1 0 1 0 1

How to rearrange matrix column into block using for loop?

I am working on a project where i have used a image whose size is (512x512)then i have divided the whole image by 8 so that there will be 64x64 block then i have rearranged each 8x8 image patch into a single column and my new size is 64x4069.
Now i want to get back the original size i.e 512x512,please help me how to get back it using for loop instead of 'col2im'
a=imread('lena.png');
b=double(a);
[m,n] = size(b);
bl=8;
br=m/bl;
bc=n/bl;
out = zeros(size(reshape(b,bl*bl,[])));
count = 1;
for i = 1:br
for j= 1:bc
block = b((j-1)*bl + 1:(j-1)*bl + bl, (i-1)*bl + 1:(i-1)*bl + bl);
out(:,count) = block(:);
count = count + 1;
end
end
This is basically your script written backwards. Some assumptions have to be made, because not every rectangular matrix can arise from the process you described, and also because the dimensions of the original matrix cannot be uniquely determined from the set of blocks. So, I assume that the original matrix was a square one, and the blocks were squares as well.
By the way, in your code you use j in the formula for row indices and i for columns; I assumed this was a mistake, and amended it below.
out = kron((0:3)', 1:16); % for testing; you would have a 64x4096 matrix here
[m,n] = size(out);
osize = sqrt(n*m);
bl = sqrt(m);
br = osize/bl;
bc = br;
original = zeros(osize);
count = 1;
for i = 1:br
for j = 1:bc
block = zeros(bl);
block(:) = out(:,count);
original(1+(i-1)*bl : i*bl, 1+(j-1)*bl : j*bl) = block;
count = count + 1;
end
end
Input for testing:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32
3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48
Output:
0 2 0 4 0 6 0 8
1 3 2 6 3 9 4 12
0 10 0 12 0 14 0 16
5 15 6 18 7 21 8 24
0 18 0 20 0 22 0 24
9 27 10 30 11 33 12 36
0 26 0 28 0 30 0 32
13 39 14 42 15 45 16 48

MATLAB vectorize

I was wondering if anyone could help me vectorize this piece of code.
fr_bw is a matrix.
for i=1:height
for j=1:width
[min_w, min_w_index] = min(w(i,j,:));
mean(i,j,min_w_index) = double(fr_bw(i,j));
sd(i,j,min_w_index) = sd_init;
end
end
I can't help you with this sif (match == 0) stuff -- if it's supposed to be if (match == 0) you're not changing match so it could be brought outside the loop.
Otherwise, how about this:
[min_w, min_w_index] = min(w, [], 3);
r = repmat((1:height)',1,width);
c = repmat(1:width,height,1);
ind = sub2ind(size(w),r(:),c(:),min_w_index(:));
w_mean(ind) = double(fr_bw);
w_sd(ind) = repmat(sd_init,height,width);
(Please note that mean is a built-in function so I renamed your variables to w_mean and w_sd.)
The sub2ind call gives you linear indices that correspond to subscripts. (Direct subscripts won't work; z([a1 a2 a3],[b1 b2 b3],[c1 c2 c3]) refers to 27 elements in the z array with subscripts that are the cartesian product of the specified subscripts, rather than z(a1,b1,c1) and z(a2,b2,c2) and z(a3,b3,c3) that you might expect.)
Here's an illustration of this technique:
>> height = 6; width = 4;
>> w = randi(1000,height,width,2)
w(:,:,1) =
426 599 69 719
313 471 320 969
162 696 531 532
179 700 655 326
423 639 408 106
95 34 820 611
w(:,:,2) =
779 441 638 696
424 528 958 68
91 458 241 255
267 876 677 225
154 519 290 668
282 944 672 845
>> [min_w, min_w_index] = min(w, [], 3);
>> min_w_index
min_w_index =
1 2 1 2
1 1 1 2
2 2 2 2
1 1 1 2
2 2 2 1
1 1 2 1
>> z = zeros(height,width,2);
>> r = repmat((1:height)',1,width);
>> c = repmat(1:width,height,1);
>> ind = sub2ind(size(w),r(:),c(:),min_w_index(:));
>> z(ind) = 1
z(:,:,1) =
1 0 1 0
1 1 1 0
0 0 0 0
1 1 1 0
0 0 0 1
1 1 0 1
z(:,:,2) =
0 1 0 1
0 0 0 1
1 1 1 1
0 0 0 1
1 1 1 0
0 0 1 0
A few comments on your code:
Did you mean if rather than sif?
The code isn't replicable at the moment, since you haven't provided examples of the variables w, fr_bw and sd_init. This makes it tricky to give an exact answer.
It looks like you are assigning things to a variable named mean. This will shadow the mean function, and probably cause you grief.
I'm just guessing, but I don't think double does what you think it does. You don't need to convert individual elements of a numeric matrix to type double; they are already the correct type. (On the other hand, if fr_bw is a different type, say integers, then you should create a new variable dbl_fr_bw = double(fr_bw); before the loops.
You might need to adjust the dimension over which you calculate the minimums, but the first line of the loop can be replaced with
[min_w, min_w_index] = min(w, [], 3)
The second line with
mean_values(:, :, min_w_index) = double(fr_bw)
Not sure about the third line, since I don't know what sd_init is/does.

How to compare a matrix element with its neighbours without using a loop in MATLAB?

I have a matrix in MATLAB. I want to check the 4-connected neighbours (left, right, top, bottom) for every element. If the current element is less than any of the neighbours then we set it to zero otherwise it will keep its value. It can easily be done with loop, but it is very expensive as I have thousands of these matrices.
You might recognize it as nonmaxima suppression after edge detection.
If you have the image processing toolbox, you can do this with a morpological dilation to find local maxima and suppress all other elements.
array = magic(6); %# make some data
msk = [0 1 0;1 0 1;0 1 0]; %# make a 4-neighbour mask
%# dilation will replace the center pixel with the
%# maximum of its neighbors
maxNeighbour = imdilate(array,msk);
%# set pix to zero if less than neighbors
array(array<maxNeighbour) = 0;
array =
35 0 0 26 0 0
0 32 0 0 0 25
31 0 0 0 27 0
0 0 0 0 0 0
30 0 34 0 0 16
0 36 0 0 18 0
edited to use the same data as #gnovice, and to fix the code
One way to do this is with the function NLFILTER from the Image Processing Toolbox, which applies a given function to each M-by-N block of a matrix:
>> A = magic(6) %# A sample matrix
A =
35 1 6 26 19 24
3 32 7 21 23 25
31 9 2 22 27 20
8 28 33 17 10 15
30 5 34 12 14 16
4 36 29 13 18 11
>> B = nlfilter(A,[3 3],#(b) b(5)*all(b(5) >= b([2 4 6 8])))
B =
35 0 0 26 0 0
0 32 0 0 0 25
31 0 0 0 27 0
0 0 0 0 0 0
30 0 34 0 0 16
0 36 0 0 18 0
The above code defines an anonymous function which uses linear indexing to get the center element of a 3-by-3 submatrix b(5) and compare it to its 4-connected neighbors b([2 4 6 8]). The value in the center element is multiplied by the logical result returned by the function ALL, which is 1 when the center element is larger than all of its nearest neighbors and 0 otherwise.
If you don't have access to the Image Processing Toolbox, another way to accomplish this is by constructing four matrices representing the top, right, bottom and left first differences for each point and then searching for corresponding elements in all four matrices that are non-negative (i.e. the element exceeds all of its neighbours).
Here's the idea broken down...
Generate some test data:
>> sizeA = 3;
A = randi(255, sizeA)
A =
254 131 94
135 10 124
105 191 84
Pad the borders with zero-elements:
>> A2 = zeros(sizeA+2) * -Inf;
A2(2:end-1,2:end-1) = A
A2 =
0 0 0 0 0
0 254 131 94 0
0 135 10 124 0
0 105 191 84 0
0 0 0 0 0
Construct the four first-difference matrices:
>> leftDiff = A2(2:end-1,2:end-1) - A2(2:end-1,1:end-2)
leftDiff =
254 -123 -37
135 -125 114
105 86 -107
>> topDiff = A2(2:end-1,2:end-1) - A2(1:end-2,2:end-1)
topDiff =
254 131 94
-119 -121 30
-30 181 -40
>> rightDiff = A2(2:end-1,2:end-1) - A2(2:end-1,3:end)
rightDiff =
123 37 94
125 -114 124
-86 107 84
>> bottomDiff = A2(2:end-1,2:end-1) - A2(3:end,2:end-1)
bottomDiff =
119 121 -30
30 -181 40
105 191 84
Find the elements that exceed all of the neighbours:
indexKeep = find(leftDiff >= 0 & topDiff >= 0 & rightDiff >= 0 & bottomDiff >= 0)
Create the resulting matrix:
>> B = zeros(sizeA);
B(indexKeep) = A(indexKeep)
B =
254 0 0
0 0 124
0 191 0
After wrapping this all into a function and testing it on 1000 random 100x100 matrices, the algorithm appears to be quite fast:
>> tic;
for ii = 1:1000
A = randi(255, 100);
B = test(A);
end; toc
Elapsed time is 0.861121 seconds.