I want to change the backgroung color of image into transparent and then work with the pixels of it
5 views (last 30 days)
Show older comments
Is it possible to crop a part of the image automatically without drawing the circle or any other shape around it?
I already have the special shape (nearly a circle) and black backround color around it, but would like to only have the special shape part of it, which is mostly not black. (picture attached -> komplett_S1.jpg) Because I have to load different images, I can not only "draw" one shape and it fits every image, right?
At the moment I cut the picture to a "square" which has black spots at the corners of the picture which I want to cut too.
I am able to change the original colored image into grayscale, into binary image and then draw a circle around the biggest blob. The aim is, to "cut" this circle for the image in original color and grayscale too. (picture2) When changing the backgroungcolor of the picture there is no transparency which I wish to have and the edges are not clear as drawed by the circle in "Biggest Blob". (picture3 and picture4)
In my nex step I would like to sum the small dots inside the special shape (circle) which are brighter than a special value. All the black spots inside the red circle drawn should be deleted or better to say, summed up for another value (for example tfa).
But at first I want to "cut" and show the special shape (image cutted at the red circle in grayscale and original color) of the image. Is there any way for doing that?
The grayscale picture which I want to work with in the future should be shown at the top in the middle and the original image in the right top corner (picture5).
Many thanks for your help!
2 Comments
DGM
on 11 Apr 2024
Edited: DGM
on 11 Apr 2024
For the purposes of processing, there is no need to crop or make anything transparent. Nothing in base MATLAB or IPT will handle an image with attached alpha data, so making a transparent image only creates a new problem.
Create a mask which describes the colored regions you want to analyze, (e.g. such that each region is represented by one blob). You can then count the blobs or find their average intensity or do whatever you want on them.
If you want to find information about interstitial spaces which may include gaps at the perimeter, perhaps you can consider the difference of the aforementioned mask and its convex hull.
What are the specific statistics that are needed? Blob count? Area? What is the appropriate brightness metric/threshold? Do green regions which aren't bright enough count as black regions?
Accepted Answer
DGM
on 12 Apr 2024
I'm still confused as to what information is important, so I'm just going to throw out what I have in notes. This is in part based on @Image Analyst's first link, so you might want to read that first.
This tries to get a mask which has all the green regions (cells?) selected individually. There are a couple very dark cells which are missed, and there are a number of cells which are split by dark marks/cracks/scratches.
A big part of the challenge is dealing with the wide variation in local contrast. I decided to upsample the image so I had enough resolution to more reliably isolate the cells.
% the original image
rgbpict = imread('komplett_S1.jpg');
% a grayscale copy for analysis
% i don't know what grayscale metric is appropriate
% so i'm just going to use luma
lumapict = im2gray(rgbpict);
% a grayscale copy for segmentation
% V has more consistent contrast than Y here
valuepict = max(rgbpict,[],3);
% crop to the bounding box to reduce the image area
% and for the sake of symmetry
mk = imbinarize(valuepict);
mk = bwareafilt(mk,1);
mk = bwconvhull(mk);
mk = imdilate(mk,ones(5));
[hullmask yrange xrange] = crop2box(mk);
valuepict = valuepict(yrange,xrange);
lumapict = lumapict(yrange,xrange);
% resize so we have more resolution to work with when filtering
% this can help avoid bridges/breaks, since filters can only be integer-sized
upsamplefactor = 2;
valuepict = imresize(valuepict,upsamplefactor);
hullmask = imresize(hullmask,upsamplefactor);
lumapict = imresize(lumapict,upsamplefactor);
% try to make the local contrast uniformly adequate
flatpict = imflatfield(valuepict,5,hullmask);
flatpict = imadjust(flatpict,stretchlim(flatpict,0.05));
% try to get a crude mask representing the background
bgmask = imopen(flatpict,strel('disk',3,0));
bgmask = bgmask > 200;
bgmask = ~bwareaopen(~bgmask,25);
bgmask = ~imfill(bgmask,'holes');
This is the point at which the watershed segmentation starts to follow the article.
% get the distance map
D = -bwdist(bgmask);
% split blobs as shown in the blog article
regionmin = imextendedmin(D,2); % the basin minima (ideally one per cell)
D = imimposemin(D,regionmin);
ridgelines = watershed(D);
splitmask = ~bgmask;
splitmask(ridgelines == 0) = false;
% visualize the segmentation
segview = imfuse(regionmin,~bgmask);
imshow(segview,'border','tight')
set(gca,'xlim',[91 402],'ylim',[1243 1554]) % zoom in so it's visible on the forum
% view the finished mask
imshow(splitmask,'border','tight')
set(gca,'xlim',[91 402],'ylim',[1243 1554]) % zoom in so it's visible on the forum
Now that you have a mask where all the cells are separated from each other, you can continue to refine the mask if you want, or you can start using it to get information about the image. In this case, I'm just going to get the number of blobs (cells and cell fragments) in the mask, and the distribution of their average luma.
% at this point, you can count the blobs in the mask
% or you can get rid of small blobs (e.g. bwareaopen())
% get info about a grayscale image
% based on the regions defined in the mask
% i don't know what statistics are important
% so i'm just going to pick average gray level (i.e. mean luma)
S = regionprops(splitmask,lumapict,'meanintensity');
% the number of blobs
nblobs = numel(S)
% do something with the region information
histogram(vertcat(S.MeanIntensity))
If you want to look at black regions (e.g. within the convex hull of the section, you can use the masks we've already created and then process them as is appropriate for your needs.
% maybe you want to look at large interstitial spaces or something
gapmask = hullmask & ~splitmask;
gapmaskopened = imopen(gapmask,strel('disk',9)); % maybe only look at thick parts
% visualize the opening of the gap mask
gmview = imfuse(gapmask,gapmaskopened);
imshow(gmview,'border','tight')
I don't know how much of that is useful to you. Note that I attached one of the ancillary functions that I used in this example.
2 Comments
DGM
on 16 Apr 2024
1: The dark blobs (for some definition of "dark blobs") can be counted by getting the number of connected groups in the gapmaskopened. That could be done using bwlabel():
[Labelarray nblobs] = bwlabel(gapmaskopened);
bwconncomp(),
CC = bwconncomp(gapmaskopened);
nblobs = CC.NumObjects
or regionprops()
S = regionprops(gapmaskopened,'area');
nblobs = numel(S)
Which one I use usually depends on what other things I'm also looking for. Usually I'm after properties, so if I'm going to use regionprops() anyway, I'll use that.
2: Same as #1, just use a different mask (i.e. splitmask). The same disclaimer applies regarding whether you want to do post-processing on the mask before counting.
3: The 200 is just the particular threshold value I chose to binarize the flattened image. That was just a manually picked value in uint8 scale (0-255). I'm not sure if using imbinarize() would do a good job of automatically picking this level. I generally don't trust it to work without any oversight, but it might require less oversight than a fixed threshold. You might try using it if you want more automation and find that a fixed threshold requires too much adjustment.
The inversions in the call to bwareaopen() are used because there is no corresponding function for area-based closing (i.e. removing holes with less than a specified area). The objective can be achieved by performing the inverse of the opening of the inverse of the mask.
The last inversion after the imfill() call is perhaps conceptually out of place, but it's necessary for the way the mask will be used in the call to bwdist(). We want D to be the distance from the background, so bgmask needs to be inverted.
4: I just chose 2 since It seemed adequate. Larger images just cost time and memory.
5: I have never had very good luck getting worry-free results from my attempts at watershed segmentation, so I can't fault anyone for losing hair over the challenge. I guess it's one of those things that require the kind of discretion that comes with experience I don't have.
6: What gets converted to white depends on the thresholds selected
bgmask = bgmask > 200;
... although the preprocessing steps (the flattening and opening) may also influence this.
The problem really is that from a strictly graylevel-based perspective, there are a lot of dim green cells which are dimmer than the gaps between bright green cells. It would be hard to include very dim green cells without bright green cells becoming so heavily connected that our subsequent attempts at watershed segmentation couldn't split them back apart. On the other hand, from the perspective of an extended local maximum, a lot of the dim green cells are barely maximal regions. They're surrounded by a only slightly darker perimeter which is in turn adjacent to a much more significant local maximum. So it's also hard to always get something like imextendedmas() to give us the dim cells without creating holes or subdividing bright cells. I tried to get it to pick up as much as I could, but I never could get it to pick up everything without risking the bright cells becoming grossly connected. There might be a smarter way to approach a problem like this, but I'm not exactly a pro.
7: Regarding counting blobs, that's #1&2, but as mentioned, you might need to adjust thresholds or other parameters as you go in order to make sure you're getting the segmentation that you hope to get. There may be some further post-processing that you can do using the generated masks, either to discard bright cells which are incomplete, or perhaps to extract portions of the "dark" mask which are in fact just dim green cells which didn't get counted.
More Answers (1)
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!