Downsample chrominance components of images using matlab - image

I want to decompose an image to Y,Cb,Cr components and then to perform downsampling in YCbCr domain to form the 4:2:2 format.
Code for decomposition of the image to YCbCr:
img=imread('flowers.tif');
figure(1), imshow(img);title('original image');
Y=0.299*img(:,:,1)+0.587*img(:,:,2)+0.114*img(:,:,3);
Cb=-0.1687*img(:,:,1)-0.3313*img(:,:,2)+0.5*img(:,:,3)+128;
Cr=0.5*img(:,:,1)-0.4187*img(:,:,2)-0.0813*img(:,:,3)+128;
%print Y, Cb, Cr components
figure(2), subplot (1,3,1), imshow(Y), title('Y,Cb,Cr components'),
subplot(1,3,2), imshow(Cb),subplot(1,3,3), imshow(Cr);
Now what i need to do to perform the down-sampling?

If by downsampling you specifically mean Chroma subsampling from 4:4:4 to 4:2:2, then one way to do it (and keep the original size of the channel) is to manually overwrite every other pixel with the previous value:
Cb(:, 2:2:end) = Cb(:, 1:2:end-1);
Cr(:, 2:2:end) = Cr(:, 1:2:end-1);
If you simply want to remove half of the columns, use:
Cb(:, 2:2:end) = [];
Cr(:, 2:2:end) = [];
Also in Matlab you don't need to write your own function for YCbCr conversion. Instead you can use rgb2ycbcr().

Related

Can we change the input dimensions of image in YoloV7?

I am trying to do inference in yolov7 and changing its default dimensions of 640x640 to 300x300. Though model weights is trained according to its default dimension. Can we change it , I am changing but it is throwing tensor error ?
path = 'image.png'
image = cv2.imread(path)
shape = image.shape
plt.imshow(image)
plt.show()
shape = image.shape
image = letterbox(image, 300, stride=64, auto=True)[0]
image_ = image.copy()
You should use the same image dimension used during the training process. If you would like to use an image size 300x300, you should first train the model with this dimension. The model accepts only the image dimension that is used during the training process. If you have different image sizes for inference...they should be resized to the input data size of the model before running the inference.

Auto-brightening images

I found this code for auto-brightening images to an optimum level.
% AUTOBRIGHTNESS
% -->Automatically adjusts brightness of images to optimum level.
% e.g. autobrightness('Sunset.jpg','Output.jpg')
function autobrightness(input_img,output_img)
my_limit = 0.5;
input_image=imread(input_img);
if size(input_image,3)==3
a=rgb2ntsc(input_image);
else
a=double(input_image)./255;
end
mean_adjustment = my_limit-mean(mean(a(:,:,1)));
a(:,:,1) = a(:,:,1) + mean_adjustment*(1-a(:,:,1));
if size(input_image,3)==3
a=ntsc2rgb(a);
end
imwrite(uint8(a.*255),output_img);
I want to ask, why the value of my_limit is 0.5?
How we determine that value?
Why use the 'ntsc' colorspace instead of another colorspace like hsv, lab or yCbCr?
I want to ask, why the value of my_limit is 0.5? How we determine that
value?
The color space NTSC ranges from 0 to 1 for each of its channel. So essentially 0.5 is the center. This is equivalent of choosing 127 for RGB space
Why use the 'ntsc' colorspace instead of another colorspace like hsv,
lab or yCbCr?
I believe ntsc provides 100% coverage of the color space and so the author of the code choose it. However most modern systems wont display in this color space and hence we use standard RGB for display. I used this website to come to this conclusion NTSC color space
Also, as pointed by Cris in this wikipedia page. NTSC stores Luminance and Chrominance and the author of the code is adjusting the Lumiance(brightness). I am including a modified script I used to come to these conclusions
input_img='lena_std.tif'
output_img='lena2.tif'
my_limit = 0.5;
input_image=imread(input_img);
if size(input_image,3)==3
a=rgb2ntsc(input_image);
k=rgb2ntsc(input_image);
else
a=double(input_image)./255;
end
mean_adjustment = my_limit-mean(mean(a(:,:,1)));
a(:,:,1) = a(:,:,1) + mean_adjustment*(1-a(:,:,1));
if size(input_image,3)==3
a=ntsc2rgb(a);
end
imwrite(uint8(a.*255),output_img);
output=uint8(a.*255);
imwrite(uint8(k.*255),'test.tif');
ntscoutput=uint8(k.*255);

How to transfer the .mat file to image file without any change?

I have a image.mat of about 4MB.
The size of some image file can also be 4MB.
Can the image.mat be transferred to image file?
I tried this, but that doesn't do the trick:
load image.mat %load Iw
imshow(mat2gray(Iw))
imwrite(Iw,'image.png');
IwNew = imread('image.png');
isequal(Iw,IwNew)
The result is 0; am I misunderstanding something?
The number in Iw are very important, so Iw can not be changed.
Actually my real problem is how to store float numbers into an image?
But MATLAB does not support Tiff 6.0, so I'll have to find some workaround.
I am doing a blind watermarking,and the decimal fraction of a number in Iw is important because it involve the information about another image.So the Iw can not be changed.
Actually,Mathematica can store floating floating-point data:
But my programs are all in MATLAB.
According to Matlab documentation:
"If A is a grayscale or RGB color image of data type double or single, then imwrite assumes that the dynamic range is [0,1] and automatically scales the data by 255 before writing it to the file as 8-bit values."
In other words: imwrite performs automatic conversion from double to uint8.
if you wish to keep the values of Iw unchanged, save it as a mat file and not as an image.
If you do want to save it as an image - there is going to be some loss of information. In this case, there are two things which need to be done:
Change the dynamic range of the matrix to [0,1]. (in your case, the range is between -0.0035 to 255.0035. Also, the matrix contain inf values).
If you want to get an equality, scale IwNew by 255, and convert it to uint8.
Code:
load image.mat %load Iw
%step 1, change the dynamic range of the image to [0,1].
%One way to do it is by using mat2gray on each channel separately.
Iw(:,:,1) = mat2gray(Iw(:,:,1));
Iw(:,:,2) = mat2gray(Iw(:,:,2));
Iw(:,:,3) = mat2gray(Iw(:,:,3));
%write the image to file
imwrite(Iw,'image.png');
%read the image
IwNew=imread('image.png');
%scale it, and convert to uint 8
Iw2 = uint8(Iw*255);
%check equality
isequal(Iw2,IwNew)
Result:
ans =
1
Alternatively, if you want to convert IwNew to double, perform the following:
%conversion to double
Iw2 = double(IwNew)/255;
Notice that in this case, the matrices won't be equal to one another,
Due to the loss of information which happened during the imwrite process (conversion from double to uint8).
Instead, they will be epsilon-close to one another, where epsilon = 0.0001.
In order to test this, write the following:
%equality check
sum(abs(Iw2(:)-Iw(:))>0.0001)
Result:
ans =
0
My MATLAB (R2010a) with the image processing toolbox is perfectly capable of storing double-valued pixel values, and retrieve them without loss of data.
Here's a shameless copy of this answer:
% Some random, data of type double
A = 7.6*rand(10);
% Construct TIFF image...
t = Tiff('test.tif', 'w');
% ...with these custom parameters...
tagstruct = struct(...
'ImageLength' , size(A,1),...
'ImageWidth' , size(A,2),...
'Compression' , Tiff.Compression.None,...
'SampleFormat' , Tiff.SampleFormat.IEEEFP,... % floating point
'Photometric' , Tiff.Photometric.MinIsBlack,...
'BitsPerSample' , 64,... % 8 bytes / double
'SamplesPerPixel' , 1,...
'PlanarConfiguration', Tiff.PlanarConfiguration.Chunky);
t.setTag(tagstruct);
% ...and write it to disk.
t.write(A);
t.close();
% Read the data actually written, and check if all
% information was indeed preserved:
B = imread('test.tif');
isequal(A,B)
Result:
ans =
1
Adjust in obvious ways if you have more than 1 channel (RGB).

Image blending with mask

I'm trying to combine the two images based on the information from the mask. I'm using the color information from the background image if the mask is 0 and color information from foreground image if the mask is 1. Because the mask and both
Images are of the same size, I would like to use logical indexing of matrices to achieve this.
My attempt:
mask = imread('mask.png');
foreground = imread('fg.jpg');
background = imread('bg.jpg');
[r,c,~]=size(mask);
A = zeros(size(mask));
for i=1:r
for j=1:c
if mask(i,j) == 0
A(i,j,:) = background(i,j,:);
end
if mask(i,j) > 0
A(i,j,:) = foreground(i,j,:);
end
end
end
imshow(A);
The result looks like a flickering blue image, but I don't want that. Please help.
You can do this a bit more concisely:
f = double(foreground).*double(mask);
b = double(background).*double(~mask);
blend = f+b;
imshow(blend, []);
Using logical indexing you could also do
foreground(logical(mask)) = 0;
background(logical(~mask)) = 0;
blend = foreground+background;
The ISNOT operator '~' inverts your matrix in the second line, so you cut out the area you would like for background.
NOTE: This works for black and white (one channel). For coloured images see rayryeng's solution.
There are two problems with your code. The first problem is that you are trying to assign colour pixels to the output image A, yet this image is only two-dimensional. You want an image with three channels, not two. In addition, the output image type you are specifying is wrong. By default, the output image A is of type double, yet you are copying values into it that aren't double... most likely unsigned 8-bit integer.
As such, cast the image to the same type as the input images. Assuming both input images are the same type, initialize your A so that:
A = zeros(size(foreground), class(foreground));
This correctly makes a colour image with the same type as any of the inputs, assuming that they're both the same type.
Now, your for loop is fine, but it's better if you do this in one shot with logical indexing. If you want to use logical indexing, create a new image that's initially blank like what you've done, but then make sure your mask has three channels to match the number of channels the other images have. After, you simply need to index into each image and set the right locations accordingly:
mask = imread('mask.png');
foreground = imread('fg.jpg');
background = imread('bg.jpg');
[r,c,d]=size(mask); %// Change
%// If your mask isn't three channels, make it so
%// Change
if d ~= 3
mask = cat(3, mask, mask, mask);
end
A = zeros(size(foreground), class(foreground)); %// Change
A(mask) = foreground(mask); %// Assign pixels to foreground
A(~mask) = background(~mask); %// Assign pixels to background
imshow(A);

Writing a greyscale video using Videowriter/avifile

I am writing a function that generates a movie mimicking a particle in a fluid. The movie is coloured and I would like to generate a grayscaled movie for the start. Right now I am using avifile instead of videowriter. Any help on changing this code to get grayscale movie? Thanks in advance.
close all;
clear variables;
colormap('gray');
vidObj=avifile('movie.avi');
for i=1:N
[nx,ny]=coordinates(Lx,Ly,Nx,Ny,[x(i),-y(i)]);
[xf,yf]=ndgrid(nx,ny);
zf=zeros(size(xf))+z(i);
% generate a frame here
[E,H]=nfmie(an,bn,xf,yf,zf,rad,ns,nm,lambda,tf_flag,cc_flag);
Ecc=sqrt(real(E(:,:,1)).^2+real(E(:,:,2)).^2+real(E(:,:,3)).^2+imag(E(:,:,1)).^2+imag(E(:,:,2)).^2+imag(E(:,:,3)).^2);
clf
imagesc(nx/rad,ny/rad,Ecc);
writetif(Ecc,i);
if i==1
cl=caxis;
else
caxis(cl)
end
axis image;
axis off;
frame=getframe(gca);
cdata_size = size(frame.cdata);
data = uint8(zeros(ceil(cdata_size(1)/4)*4,ceil(cdata_size(2)/4)*4,3));
data(1:cdata_size(1),1:cdata_size(2),1:cdata_size(3)) = [frame.cdata];
frame.cdata = data;
vidObj = addframe(vidObj,frame);
end
vidObj = close(vidObj);
For your frame data, use rgb2gray to convert a colour frame into its grayscale counterpart. As such, change this line:
data(1:cdata_size(1),1:cdata_size(2),1:cdata_size(3)) = [frame.cdata];
To these two lines:
frameGray = rgb2gray(frame.cdata);
data(1:cdata_size(1),1:cdata_size(2),1:cdata_size(3)) = ...
cat(3,frameGray,frameGray,frameGray);
The first line of the new code will convert your colour frame into a single channel grayscale image. In colour, grayscale images have all of the same values for all of the channels, which is why for the second line, cat(3,frameGray,frameGray,frameGray); is being called. This stacks three copies of the grayscale image on top of each other as a 3D matrix and you can then write this frame to your file.
You need to do this stacking because when writing a frame to file using VideoWriter, the frame must be colour (a.k.a. a 3D matrix). As such, the only workaround you have if you want to write a grayscale frame to the file is to replicate the grayscale image into each of the red, green and blue channels to create its colour equivalent.
BTW, cdata_size(3) will always be 3, as getframe's cdata structure always returns a 3D matrix.
Good luck!

Resources