How can i detect silver pixels in an image? - matlab

I'm trying to detect all the pixels with silver colors in my image.
I tried to convert the image from RGB to HSV but I saw that every silver pixel is very different from another and therefore we couldn't identify all the silver pixels.
I'm trying to understand if it's possible to use HSV to solve this problem or maybe there is a better way to detect all the silver pixels?
I'm trying to use this code that will mark all the silver pixels in blue:
I chose the Hue,Saturation and Value (HSV) according to wikipedia to identify the silver color, therefore Hue doesn't matter, Saturation is 0-0.1 and Value is 0.65-0.85.
clear all; close all; clc; imtool close all;
im = imread('image.jpg');
size_x = size(im,1);
size_y = size(im,2);
size_z = size(im,3);
hsv = rgb2hsv(im);
for i = 1:size_x
for j = 1:size_y
if (hsv(i,j,2) < 0.1) && (hsv(i,j,3) > 0.65) && (hsv(i,j,3) < 0.85)
im(i,j,1) = 0;
im(i,j,2) = 0;
im(i,j,3) = 255;
end
end
end
imtool(im);
with this Image:
Thanks in advance.

The difference between a white pixel and a grey pixel is the amount of light that falls on it. You can see this in your picture, the whitish background changes brightness from left to right, going from middle grey to light grey. We perceive it all as white.
The same is true for silver. Silver is a white/grey color, but we perceive it differently because it has specular reflections. This is what characterizes metal surfaces. But the color of the pixels themselves are just grey and white.
That is to say, you cannot use color segmentation to distinguish silver contacts from white paper.
I would suggest you look for the shapes of interest, rather than the colors of interest.

Related

Background and foreground color changes

Hello i need to make background color black and foreground color white. As u can see i did this with transfering image to 2 dimension. I want to make this color changes in 3 dimension, so we are nor allowed to transfer it bw. Is there any way to do this ?
logo=imread('logo.png');
subplot(2,2,1);
imshow(logo);
b=rgb2gray(logo);
subplot(2,2,2);
imshow(b);
c=im2bw(b,0.92)
subplot(2,2,3);
imshow(c);
c = 1-c;
subplot(2,2,4);
imshow(c);
Preface:
To set the pixel to white or black each layer of the pixel needs to be set to an intensity value of 0 (black) or 255 (white).
White Pixel → rgb(255,255,255)
Black Pixel → rgb(0,0,0)
The colon can be used to obtain all the indices in the 3rd dimension (grab all the layers). To grab one RGB-pixel in the top-left corner of the image:
RGB_Pixel = Image(1,1,:);
Method 1:
If you wish to retain the three colour channels you can use matrix indexing to change the white background to black. Matrix indexing can also be used to change anywhere that isn't white to white. This method may, unfortunately, break down if you have a coloured component with a 255 intensity component. This doesn't seem to be the case for your image though. You can use method 2 for a more safe approach.
logo = imread('logo.png');
[Image_Height,Image_Width,Depth]= size(logo);
new_logo = zeros(Image_Height,Image_Width,Depth);
new_logo(logo == 255) = 0;
new_logo(logo ~= 255) = 255;
imshow(new_logo);
Method 2:
Checks each pixel (RGB-triplet) using a set of for-loops that scan through the entire image. If the RGB-intensities of the pixel are rgb(255,255,255) then the pixels are set to 0 (black). If the RGB-intensities of the pixel are anything else the pixels are set to 255 (white). The ~ismember() function is used to check if the RGB-pixel has an intensity that is not 255 (not-white).
logo = imread('logo.png');
%Grabbing the size of the image%
[Image_Height,Image_Width,~]= size(logo);
for Row = 1: Image_Height
for Column = 1: Image_Width
%Grabbing RGB pixel%
RGB_Pixel = logo(Row,Column,:);
if(~ismember(255,RGB_Pixel))
%RGB pixel is white change
logo(Row,Column,:) = 255;
else
%RGB pixel is coloured change to black%
logo(Row,Column,:) = 0;
end
end
end
imshow(logo);
Using the repmat() function is also a great solution that the above comment suggested. Which possibly may be the quickest method since you already have the code that generates one layer from the greyscale image.
Ran using MATLAB R2019b

How to change green pixels to gold color in peppers image with matlab?

I want to change green color pixels to gold color in peppers.png image in Matlab.
How can I do this task? Thanks very much for your help
Introduction
Using the HSV colorspace gives a better intuition of detecting a certain color hue and manipulating it. For further information, read the following answer.
Solution
Given an image in hsv format, each color has a certain range which it can reside in. In the peppers image, the hue channel of green peppers is in the range [40/360,180/360] (more or less). Also, the color gold can be identified by a hue value of 0.125 and 'V' value of 0.8. Therefore, a good way to change green to gold in a certain picture will be as follows:
transform the image to hsv.
locate green colors by identifying hue value between the range [40/360,180/360].
changing their first channel to 0.125, and their second channel to 0.8.
transform back to rgb.
*comment: instead of fully changing the third channel of the green pixels to 0.8, it will be better to perform an averaging of 0.8 with the originally stored value, to get a more natural effect (see code below).
Code
%reads the image. converts it to hsv.
I = imread('peppers.png');
hsv = rgb2hsv(I);
%locate pixels with green color
GREEN_RANGE = [40,180]/360;
greenAreasMask = hsv(:,:,1)>GREEN_RANGE(1) & hsv(:,:,1) < GREEN_RANGE(2);
%change their hue value to 0.125
HUE_FOR_GOLD = 0.12;
V_FOR_GOLD = 0.8;
goldHsv1 = hsv(:,:,1);
goldHsv1(greenAreasMask)=HUE_FOR_GOLD;
goldHsv3 = hsv(:,:,3);
goldHsv3(greenAreasMask)=V_FOR_GOLD;
newHsv = hsv;
newHsv(:,:,1) = goldHsv1;
newHsv(:,:,3) = newHsv(:,:,3) + goldHsv3 / 2;
%transform back to RGB
res = hsv2rgb(newHsv);
Result
As you can see, the green pixels became more goldish.
There is a room for improvement, but I think that this would be a good start for you. To improve the result you can modify the green and gold HSV values, and use morphological operations on greenAreasMask.

Matlab - How to detect green color on image?

I'm working in project that basically I have to detect the threes on image and delete the other information. I used HSV as segmentation and the function regionprops to detect each element. It works fine, but in same cases that has house roofs, they aren't deleted because the value of Hue is similar to the threes. So far, this is the result:
To remove the roofs, I thought that maybe is possible detecting the color green in each region detected. If the region dont have 70% of green (for example) that region is deleted. How can I do that? How Can I detect only the green color of the image?
Solution Explanation
Evaluating the level of green in a patch is an interesting idea. I suggest the following approach:
convert your patches from RGB to HSV color system. In the HSV color system it is easier to evaluate the hue (or - the color) of each pixel, by examining the first channel.
Find the range for green color in the hue system. In our case it is about [65/360,170/360], as can be seen here:
for each patch, calculate how many pixels have the hue value which is in the green range, and divide by the size of the connected component.
Code Expamle
The following function evaluate the "level of greenness" in a patch:
function [ res ] = evaluateLevelOfGreen( rgbPatch )
%EVALUATELEVELOFGREEN Summary of this function goes here
% Detailed explanation goes here
%determines the green threshold in the hue channel
GREEN_RANGE = [65,170]/360;
INTENSITY_T = 0.1;
%converts to HSV color space
hsv = rgb2hsv(rgbPatch);
%generate a region of intereset (only areas which aren't black)
relevanceMask = rgb2gray(rgbPatch)>0;
%finds pixels within the specified range in the H and V channels
greenAreasMask = hsv(:,:,1)>GREEN_RANGE(1) & hsv(:,:,1) < GREEN_RANGE(2) & hsv(:,:,3) > INTENSITY_T;
%returns the mean in thie relevance mask
res = sum(greenAreasMask(:)) / sum(relevanceMask(:));
end
Results
When using on green patches:
greenPatch1 = imread('g1.PNG');
evaluateLevelOfGreen(greenPatch1)
greenPatch2 = imread('g2.PNG');
evaluateLevelOfGreen(greenPatch2)
greenPatch3 = imread('g3.PNG');
evaluateLevelOfGreen(greenPatch3)
Results:
ans = 0.8230
ans = 0.8340
ans = 0.6030
when using on non green patches:
nonGreenPatch1 = imread('ng1.PNG');
evaluateLevelOfGreen(nonGreenPatch1)
result:
ans = 0.0197

Finding dark purple pixels in an image

I am doing a research for my higher studies in automation. I have done the automation part of the microscope but I need help in MATLAB. An example of what I would like to segment is shown here:
I need to extract the dark purple pixels from this image and only display that in a figure. It is almost like colour based segmentation but I just want to only take the dark purple pixel from the whole image.
What would I do in this case?
Here's something to get you started. Let's go with the theme of colour segmentation where you only want to extract pixels that are of a deep purple. I would like to point you to the HSV colour space before we get started. The HSV colour space is ideal for representing colours in a way that is most intuitive to humans. We tend to describe colours by their dominant colour, followed by attributes such as how washed out or how pure the colour is, and how bright or dark the colour is. The dominant colour is represented by the Hue, the appearance of how washed out or how pure the colour is is represented by the Saturation and the intensity of the colour is represented by the Value, and hence Hue-Saturation-Value, or the HSV colour space.
We can transform a RGB image so that it becomes HSV by rgb2hsv. This will return a 3D matrix that has the hue, saturation and value as 2D slices in a 3D matrix, much like a RGB image where each slices represents the red, green and blue channels. Let's see what each component looks like once we transform the image into HSV:
im = imread('https://www.cdc.gov/dpdx/images/malaria/ovale/Po_gametocyte_thickB.jpg');
hsv = rgb2hsv(im2double(im));
figure;
for idx = 1 : 3
subplot(1,3,idx);
imshow(hsv(:,:,idx));
end
The first line of code reads in an image from a URL. I'm going to use the one that Hoki referred you to, as it's the most simplest one to deal with. For self-containment, this is what the original image looks like:
Once we do this, we convert the image into the HSV colour space. It is important that you convert the image to double precision and you normalize each component to [0,1], and that is performed by im2double. Next, we spawn a new figure, and place each component in a single row over three columns. The first column represents the hue, next column the saturation and finally the last column being the value. This is the figure that we see:
With the first figure, it looks like the dominant colour is purple, whether it's a light shade or a dark shade of the colour, so the hue won't help us here. If you look at a HSV colour wheel:
(source: hobbitsandhobos.com)
Normalize the wheel so that it falls between [0,1] instead of 0 to 360 degrees. The hue is actually represented as degrees due to the nature of the colour space, but MATLAB normalizes this to [0,1]. You can see that purple falls within a hue of [0.6,0.8], which corresponds to the first figure I showed you that displays the hue for our image. If you examine the pixels around the image, they fluctuate between this range. Therefore, the hue won't help us much here.
What will certainly help us are the saturation and value components. If you take a look, the deep purple pixels have a higher saturation than the rest of the background, which makes sense because the deep purple has a much more pure version of purple than the rest of the background. For the value, you can see that the brightness of the dark purple is darker than the background.
We can use these two points as an exploit to segment out the purple colour in the image. The easiest thing to do would be to threshold the saturation and value planes so that any values that are within a certain range you keep while those that are outside you throw away. Therefore, you can do something like this:
sThresh = hsv(:,:,2) > 0.6 & hsv(:,:,2) < 0.9;
vThresh = hsv(:,:,3) > 0.4 & hsv(:,:,3) < 0.65;
I used impixelinfo and I hovered my mouse over the saturation and value components to examine what the values were for the deep purple regions. It looks like those pixels that are deep purple have a saturation value between 0.6 and 0.9, while the value component has values between 0.4 and 0.65. The above code will create two binary masks where true means that the pixel satisfies our criteria while false means it doesn't. Because I want to combine both things together and not leave any stone unturned, let's logical OR the masks together for the final result:
figure;
result = sThresh | vThresh;
imshow(result);
We will also show the result too. This is what we get:
As you can see, this does a pretty good job, but we have remnants of the red arrow that we don't want in the final result. To do a bit of cleanup, we can use morphology - specifically an opening filter of a small window so that we don't affect the pixels that we want as much. We can use imopen to perform our opening operation for us. A morphological opening removes isolated pixels that appear around your image. You use what is called a structuring element that is used to look at local neighbourhoods of your image. For the basics, any pixel regions that are as small as the shape that is contained within the structuring element get removed. Because we want to preserve the shape of the other objects, we can try using a 5 x 5 disk structuring element to clean these pixels up:
figure;
se = strel('disk', 2, 0);
final = imopen(result, se);
imshow(final);
This is what we get:
Not bad! There are some holes that we need to patch up, so let's fill in those holes with imfill:
figure;
final_noholes = imfill(final, 'holes');
imshow(final_noholes);
This is what we get:
OK! So we have our mask. The last thing we need to do is present the image so that you only show the deep purple colours from the original image, and nothing else. That can easily be achieved with bsxfun:
figure;
out = bsxfun(#times, im, uint8(final_noholes));
imshow(out);
The above operation takes your mask, and multiplies every pixel in your image by this mask. One small thing I'd like to point out is that the mask we found in the previous step needs to be cast to uint8, because bsxfun requires that the multiplication (or whatever operation you perform) need to be the same type. We replicate this mask in 3D so that you mask out the unwanted RGB pixels and only keep the ones you are looking for.
This is what we finally get:
As you can see, it isn't perfect, but it's certainly enough to get you started. Those thresholds are what are important, but with some very simple thresholding, I extracted most of the purple pixels out.
To make it easier for you, here's the code that I wrote above that can easily be copied and pasted into MATLAB for you to run:
clear all; close all; clc;
im = imread('https://www.cdc.gov/dpdx/images/malaria/ovale/Po_gametocyte_thickB.jpg');
hsv = rgb2hsv(im2double(im));
figure;
for idx = 1 : 3
subplot(1,3,idx);
imshow(hsv(:,:,idx));
end
sThresh = hsv(:,:,2) > 0.6 & hsv(:,:,2) < 0.9;
vThresh = hsv(:,:,3) > 0.4 & hsv(:,:,3) < 0.65;
figure;
result = sThresh | vThresh;
imshow(result);
figure;
se = strel('disk', 2, 0);
final = imopen(result, se);
imshow(final);
figure;
final_noholes = imfill(final, 'holes');
imshow(final_noholes);
figure;
out = bsxfun(#times, im, uint8(final_noholes));
imshow(out);
Good luck!
Try this:
function main
clc,clear
A = imread('https://www.cdc.gov/dpdx/images/malaria/ovale/Po_gametocyte_thickB.jpg');
subplot(1,2,1)
imshow(A)
RGB = [230 210 200]; % color you want
e = 40; % color shift
B = pix_in(A,RGB,e);
B = B + 255.*uint8(~B); % choosing white background
subplot(1,2,2)
imshow(B)
end
function B = pix_in(A,RGB,e)
% select specific pixels in image
% A - color image (3D matrix uint8)
% RGB - [R G B] - color to select
% e - color shift/deviation
A = double(A); % for same class operations (RGB - double)
[m, n, ~] = size(A);
RGB = reshape(RGB,1,1,3);
RGB = repmat(RGB,m,n,1); % creating 3D matrix
b = abs(A-RGB) < e; % logical 3D
b = sum(b,3) == 3; % if [R,G,B] of a pixel in range
B = A.*repmat(b,1,1,3); % selecting pixels those in range
B = uint8(B);
end

Extract black objects from color background

It is easy for human eyes to tell black from other colors. But how about computers?
I printed some color blocks on the normal A4 paper. Since there are three kinds of ink to compose a color image, cyan, magenta and yellow, I set the color of each block C=20%, C=30%, C=40%, C=50% and rest of two colors are 0. That is the first column of my source image. So far, no black (K of CMYK) ink is supposed to print. After that, I set the color of each dot K=100% and rest colors are 0 to print black dots.
You may feel my image is weird and awful. In fact, the image is magnified 30 times and how the ink cheat our eyes can be seen clearly. The color strips hamper me to recognize these black dots (the dot is printed as just one pixel in 800 dpi). Without the color background, I used to blur and do canny edge detector to extract the edge. However, when adding color background, simply do grayscale and edge detector cannot get good results because of the strips. How will my eyes do in order to solve such problems?
I determined to check the brightness of source image. I referred this article and formula:
brightness = sqrt( 0.299 R * R + 0.587 G * G + 0.114 B * B )
The brightness is more close to human perception and it works very well in the yellow background because the brightness of yellow is the highest compared with cyan and magenta. But how to make cyan and magenta strips as bright as possible? The expected result is that all the strips disappear.
More complicated image:
C=40%, M=40%
C=40%, Y=40%
Y=40%, M=40%
FFT result of C=40%, Y=40% brightness image
Anyone can give me some hints to remove the color strips?
#natan I tried FFT method you suggested me, but I was not lucky to get peak at both axis x and y. In order to plot the frequency as you did, I resized my image to square.
I would convert the image to the HSV colour space and then use the Value channel. This basically separates colour and brightness information.
This is the 50% cyan image
Then you can just do a simple threshold to isolate the dots.
I just did this very quickly and im sure you could get better results. Maybe find contours in the image and then remove any contours with a small area, to filter any remaining noise.
After inspecting the images, I decided that a robust threshold will be more simple than anything. For example, looking at the C=40%, M=40% photo, I first inverted the intensities so black (the signal) will be white just using
im=(abs(255-im));
we can inspect its RGB histograms using this :
hist(reshape(single(im),[],3),min(single(im(:))):max(single(im(:))));
colormap([1 0 0; 0 1 0; 0 0 1]);
so we see that there is a large contribution to some middle intensity whereas the "signal" which is now white, is mostly separated to higher value. I then applied a simple thresholds as follows:
thr = #(d) (max([min(max(d,[],1)) min(max(d,[],2))])) ;
for n=1:size(im,3)
imt(:,:,n)=im(:,:,n).*uint8(im(:,:,n)>1.1*thr(im(:,:,n)));
end
imt=rgb2gray(imt);
and got rid of objects smaller than some typical area size
min_dot_area=20;
bw=bwareaopen(imt>0,min_dot_area);
imagesc(bw);
colormap(flipud(bone));
here's the result together with the original image:
The origin of this threshold is from this code I wrote that assumed sparse signals in the form of 2-D peaks or blobs in a noisy background. By sparse I meant that there's no pile up of peaks. In that case, when projecting max(image) on the x or y axis (by (max(im,[],1) or (max(im,[],1) you get a good measure of the background. That is because you take the minimal intensity of the max(im) vector.
If you want to look at this differently you can look at the histogram of the intensities of the image. The background is supposed to be a normal distribution of some kind around some intensity, the signal should be higher than that intensity, but with much lower # of occurrences. By finding max(im) of one of the axes (x or y) you discover what was the maximal noise level.
You'll see that the threshold picks that point in the histogram where there are still some noise above it, but ALL the signal is above it too. that's why I adjusted it to be 1.1*thr. Last, there are many fancier ways to obtain a robust threshold, this is a quick and dirty way that in my view is good enough...
Thanks to everyone for posting his answer! After some search and attempt, I also come up with an adaptive method to extract these black dots from the color background. It seems that considering only the brightness could not solve the problem perfectly. Therefore natan's method which calculates and analyzes the RGB histogram is more robust. Unfortunately, I still cannot obtain a robust threshold to extract the black dots in other color samples, because things are getting more and more unpredictable when we add deeper color (e.g. Cyan > 60) or mix two colors together (e.g. Cyan = 50, Magenta = 50).
One day, I google "extract color" and TinEye's color extraction and color thief inspire me. Both of them are very cool application and the image processed by the former website is exactly what I want. So I determine to implement a similar stuff on my own. The algorithm I used here is k-means clustering. And some other related key words to search may be color palette, color quantation and getting dominant color.
I firstly apply Gaussian filter to smooth the image.
GaussianBlur(img, img, Size(5, 5), 0, 0);
OpenCV has kmeans function and it saves me a lot of time on coding. I modify this code.
// Input data should be float32
Mat samples(img.rows * img.cols, 3, CV_32F);
for (int i = 0; i < img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
for (int z = 0; z < 3; z++) {
samples.at<float>(i + j * img.rows, z) = img.at<Vec3b>(i, j)[z];
}
}
}
// Select the number of clusters
int clusterCount = 4;
Mat labels;
int attempts = 1;
Mat centers;
kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10, 0.1), attempts, KMEANS_PP_CENTERS, centers);
// Draw clustered result
Mat cluster(img.size(), img.type());
for (int i = 0; i < img.rows; i++) {
for(int j = 0; j < img.cols; j++) {
int cluster_idx = labels.at<int>(i + j * img.rows, 0);
cluster.at<Vec3b>(i, j)[0] = centers.at<float>(cluster_idx, 0);
cluster.at<Vec3b>(i, j)[1] = centers.at<float>(cluster_idx, 1);
cluster.at<Vec3b>(i, j)[2] = centers.at<float>(cluster_idx, 2);
}
}
imshow("clustered image", cluster);
// Check centers' RGB value
cout << centers;
After clustering, I convert the result to grayscale and find the darkest color which is more likely to be the color of the black dots.
// Find the minimum value
cvtColor(cluster, cluster, CV_RGB2GRAY);
Mat dot = Mat::zeros(img.size(), CV_8UC1);
cluster.copyTo(dot);
int minVal = (int)dot.at<uchar>(dot.cols / 2, dot.rows / 2);
for (int i = 0; i < dot.rows; i += 3) {
for (int j = 0; j < dot.cols; j += 3) {
if ((int)dot.at<uchar>(i, j) < minVal) {
minVal = (int)dot.at<uchar>(i, j);
}
}
}
inRange(dot, minVal - 5 , minVal + 5, dot);
imshow("dot", dot);
Let's test two images.
(clusterCount = 4)
(clusterCount = 5)
One shortcoming of the k-means clustering is one fixed clusterCount cannot be applied to every image. Also clustering is not so fast for larger images. That's the issue annoys me a lot. My dirty method for better real time performance (on iPhone) is to crop 1/16 of the image and cluster the smaller area. Then compare all the pixels in the original image with each cluster center, and pick the pixel that are the nearest to the "black" color. I simply calculate euclidean distance between two RGB colors.
A simple method is to just threshold all the pixels. Here is this idea expressed in pseudo code.
for each pixel in image
if brightness < THRESHOLD
pixel = BLACK
else
pixel = WHITE
Or if you're always dealing with cyan, magenta and yellow backgrounds then maybe you might get better results with the criteria
if pixel.r < THRESHOLD and pixel.g < THRESHOLD and pixel.b < THRESHOLD
This method will only give good results for easy images where nothing except the black dots is too dark.
You can experiment with the value of THRESHOLD to find a good value for your images.
I suggest to convert to some chroma-based color space, like LCH, and adjust simultaneous thresholds on lightness and chroma. Here is the result mask for L < 50 & C < 25 for the input image:
Seems like you need adaptive thresholds since different values work best for different areas of the image.
You may also use HSV or HSL as a color space, but they are less perceptually uniform than LCH, derived from Lab.