Convert and indexed image to RGB and back without losing data - matlab

I am having trouble converting an indexed image to RGB and then back from RGB to an indexed image. For some reason, the result is different from the original. I am doing steganography so it can't work if the data is changed.
This is my code and this is the sample image:
[J map]=imread('expert.gif');
Jrgb=ind2rgb(J,map);
Jind=rgb2ind(Jrgb,map);
isequal(J,Jind)
Variables J and Jind are supposed to be equal. Why are they being detected as being different?

First of all, I'm certain that this is related to this question.
The issue is happening because if you actually look at the colormap of your loaded image:
map =
0 0 0
0.6275 0.3216 0.1765
0.4902 0.4902 0.4902
0.8039 0.5216 0.2471
0.7451 0.7451 0.7451
0.8627 0.8627 0.8627
0.9020 0.9020 0.9804
0 0 0
You'll see that the color black (0,0,0) actually exists in there twice so both index = 0 and index = 7 will resolve to black in the RGB image.
When you do the conversion back to an indexed image, MATLAB is going to use the same index for both of those (because they are obviously the same color) even if the colormap that you pass to rgb2ind is the same colormap.
That explains why the differences that you're seeing are where the transparent pixels are (around the periphery).
As far as dealing with this, I think it's a little tricky. Unfortunately the transparency (3rd output) output of imread is an empty array.
You could potentially change the input colormap so that the first and last rows aren't the same (set the last row to 1's) and then you should get back something comparable.
map(end,:) = 1;
rgb = ind2rgb(J, map);
ind = rgb2ind(rgb, map);
isequal(J, ind);
In general, due to MATLAB's limitations, GIFs with transparency may not be the best test case for playing with stenography.

Related

how to modify values of Mat according to some condition in opencv?

In Matlab a(a>50)=0 can replace all elements of a that are greater than 50 to 0. I want to do same thing with Mat in OpenCV. How to do it?
Naah. to do that, just one line:
cv::Mat img = imread('your image path');
img.setTo(0,img>50);
as simple as that.
What you want is to truncate the image with cv::threshold.
The following should do what you require:
cv::threshold(dst, dst, 50, 0, CV_THRESH_TOZERO_INV);
this is the function definition
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html?highlight=threshold#threshold
Sometimes threshold doesn't work because you can have a diferent kind of Mat. If your Mat type supports double, threshold will crash (at least in my android studio).
You can find compare here:
http://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html
So I use the function compare:
Mat mask = new Mat(yourMat.rows(), yourMat.cols(), CvType.CV_64FC1);
Mat ones = org.opencv.core.Mat.ones(yourMat.rows(), yourMat.cols(), CvType.CV_64FC1);
Scalar alpha = new Scalar(50, CvType.CV_64FC1);
Core.multiply(ones, alpha, ones);
Core.compare(yourMat, zeros, mask, Core.CMP_LT);
Here I am creating a matrix with only 50 in all points. After that i am comparing it with yourMat, using CMP_LT (Less than). So all pixels less than 50 will turn in 255 in your mask and 0 if bigger. It is a mask. So you can just:
yourMat.copyTo(yourMat, mask);
Now all the pixels bigger than 50 will be zero and all the other will have their own values.

crop an image according to what is different between 2 images

I have different images and I would like to crop them and keep only what is different between both. Here is the code I have so far.
video = VideoReader('frames.avi', 'Tag', 'my reader object');
frameFirst = read(video,1);
frameSecond = read(video, video.NumberOfFrames-1 );
imshowpair (frameSecond,frameFirst);
pause();
Well, it's tough to give you a good answer without much more detail. I think I understand what you're trying to do, and this might get you moving in the right direction. This code iterates through each pixel of the image (each pixel contains a 1x3 vector of RGB data ranging from 0 to 1), by row and column. If the difference in any of the elements of the 1x3 RGB vector exceeds some threshold (in this case, set to 0.1), we make that whole pixel black (set it to [0 0 0]). Else, we lust make it whatever the last frame was. To filter out all but those pixels that are identical, set the thresh value to 0. It goes like this:
thresh = 0.1
for ii = 1:size(frameFirst, 1)
for jj = 1:size(frameFirst, 2)
pixDiff = frameFirst{ii, jj} - frameSecond{ii, jj}
if (pixDiff(1) > thresh || pixDiff(2) > thresh || pixDiff(3) > thresh)
outputFrame = frameSecond{ii, jj};
else
outputFrame = [0 0 0];
end
end
end
I hope this does what you're looking for. Good luck!
Edit 1: Ok, I understand what you are looking for now. You need to have the indices of the bottom-right and top-left. If you already have those, just do this: frameOut = frameIn(xStart:xStop, yStart, yStop. If you need to find those points, that's harder. Let me know and I'll help you work it out.

How to convert a grayscale image to binary in MATLAB

I have to write a program that converts intensity images into black-and-white ones. I just figured I could take a value from the original matrix, I, and if it's above the mean value, make the corresponding cell in another array equal to 1, otherwise equal to zero:
for x=1:X
for y=1:Y
if I(x,y)>mean(I(:))
bw(x,y)=1;
elseif I(x,y)<mean(I(:))
bw(x,y)=0;
end
end
end
image(bw)
Unfortunately, the image I get is all black. Why?
I is in uint8, btw. 2-D Lena.tiff image
Use this :
bw = im2bw(I, graythresh(I));
Here the documentation for im2bw;
using imshow(I,[]);, doesn't evaluate the image between 0 and 255, but between min(I(:)) and max(I(:))
EDIT
You can change graythresh(I) by anyother level. You can still use the mean of your image. (Normalize between 0 and 1).
maxI = max(I(:));
minI = min(I(:));
bw = im2bw(I,(maxI - mean(I(:)))/(maxI - minI));
Use imagesc(bw) (instead of image(bw)). That automatically scales the image range.
Also, note that you can replace all your code by this vectorized, more efficient version:
bw = double(I>mean(I(:)));
One problem is that you are always using the mean untill then.
What you will want to do is do this before the loop:
myMean = mean(I(:));
And then replace all following occurences of mean(I(:)) by myMean.
Using the running mean as you currently will definitely slow things, but it will not be the reason why it becomes all black.

Matlab Bug -- Matrix Elements Keep Maxing Out

There's a peculiar bug in my code that I can't seem to figure out. For context, I is an image, a matrix, consisting of scaled values from 0 to 255. GetSpatAvg is a function that is not included here. The problem I'm facing is that the elements in ngtdm always max out at 255. When this function finishes, I get the matrix ngtdm back consisting of many many values that are 255. The code is below exactly as it shows on my computer.
function ngtdm = getNGTDM(I,d)
[rowI, colI] = size(I);
ngtdm = zeros(256, 1);
for r=1+d:rowI-d
for c=1+d:colI-d
term = I(r,c)-getSpatAvg(r,c);
ngtdm(I(r,c)+1)=ngtdm(I(r,c)+1)+term;
end
end
end
I isolated a specific value in I, 254, in the code below.
function ngtdm = getNGTDM(I,d)
[rowI, colI] = size(I);
ngtdm = zeros(256, 1);
for r=1+d:rowI-d
for c=1+d:colI-d
if(I(r,c)==254)
term = I(r,c)-getSpatAvg(r,c);
disp(term);
ngtdm(I(r,c)+1)=ngtdm(I(r,c)+1)+term;
end
end
end
end
The variable 'term', in this instance was always 222. There were 369 instances of 222. Therefore the value at ngtdm(255) (254+1=255) should be larger than 255, yet the element still maxed out at 255. When I replace term with 222 as shown below:
ngtdm(I(r,c)+1)=ngtdm(I(r,c)+1)+222;
I get the correct value, a number larger than 255.
I can't seem to figure out why my element is always maxing out at 255. Could it be something to do with the fact that the values of I are scaled between 0 and 255. I'm pretty positive that getSpatAvg is not the issue because the correct value is being returned.
Thank You
It's not a bug - it sounds like you are using a uint8 datatype. If you convert to a datatype with more bits - e.g. uint16, uint32, single, double, etc - you will not run into this problem. I guess you are working with images, as images read using imread are read in as uint8 by default to save memory. Quickest fix: use I=double(I); either at the start of your function, or on the variable I before you put it into the functions.

bitxor operation in MATLAB

I am trying to understand why the original image is not coming with this code. The resulting image receive is yellowish in color, instead of being similar to the image Img_new.
Img=imread(‘lena_color.tif’);
Img_new=rgb2gray(img);
Send=zeroes(size(Img_new);
Receive= zeroes(size(Img_new);
Mask= rand(size(Img_new);
for i=1 :256
for j=1:256
Send(i,j)=xor( Img_new(i,j),mask(i,j));
End
End
image(send);
imshow(send);
for i=1 :256
for j=1:256
receive(i,j)=xor( send(i,j),mask(i,j));
End
End
image(receive);
imshow(receive);
What am I doing wrong?
There are several problems in your code.
MATLAB is case sensitive so end and End is not the same. Same goes for receive, and send.
MATLAB has a lot of matrix-based operations so please use for loops as a last resort since most of these operations can be executed by MATLAB's optimized routines for matrices.
MATLAB's xor returns the logical xor so when it sees two values (or matrices of values) it doesn't matter if it's 234 xor 123 or 12 xor 23 since it is equivalent to 1 xor 1 and 1 xor 1. You're looking for bitxor which does the bitwise xor on each element of the matrix and I've used it in my code below. This is the only way you can retrieve the information with the pixel == xor(xor(pixel,key),key) operation (assuming that's what you want to do).
rand returns a real value from 0 - 1 ; therefore, to do a successful bitwise xor, you need numbers from 0 - 255. Hence, in my code, you'll see that mask has random values from 0-255.
Note: I've used peppers.png since it's available in MATLAB. Replace it with lena_color.tif.
%%# Load and convert the image to gray
img = imread('peppers.png');
img_new = rgb2gray(img);
%%# Get the mask matrix
mask = uint8(rand(size(img_new))*256);
%%# Get the send and receive matrix
send = bitxor(img_new,mask);
receive = bitxor(send,mask);
%%# Check and display
figure;imshow(send,[0 255]);
figure;imshow(receive,[0 255]);
Update:
%%# Get mask and img somehow (imread, etc.)
img = double(img);
mask_rgb = double(repmat(mask,[1 1 3]));
bitxor(img,mask);
If instead, you choose to make everything uint8 instead of double, then I urge you to check if you are losing data anywhere. img is uint8 so there is no loss, but if any of the values of mask is greater than 255 then making it double will lead to a loss in data.