I am trying to apply histogram normalization to create a dense color histogram.
Split the channels into R, G, B
Normalize the individual histogram
Merge
I think that this is the normal step perhaps if I am wrong please let me know. Now,
for a rainbow image as shown below I get I get max of 255 for all 3 channel and 0 as min. Using the formula for
Pixel - Min / (Max - min) * 255
I will get the same image as the original one back. What is the critical step that I am missing. Please advise me.Thank you!
REf: http://www.roborealm.com/help/Normalize.php.. I used this reference
White = (255,255,255). Black = (0,0,0). So your program finds the white background, and the black line in the bottom right.
Remove the white and change it to black. Then make your program ignore black.
images having white and black pixels cannot be normalized as such. your formula is giving you the same value. try ignoring all white and black pixels and normalize the pixels one by one.
as i see here you have a well distributed image for all channels already, so normalizing this one may not work well anyways..
Related
I have two images that I will like to combine together and fill/remove the gaps between the borders after combining. The image on the left is the edge while the image on the right is the mask. (Ignore the little patch on the right picture but it will be nice to be able to remove it too)
The expected result after combination is
but this is the current result achieved so far
I have tried different strategies from scikit-image apis, which includes:
ndi.binary_opening, ndi.binary_closing, morphology.{erosion, dilation, opening, closing} but none of them seem to work.
I think this may be a basis for a strategy...
Step 1:
Start with the "edge image". Randomly choose any white pixel. Flood fill with black using that pixel as the seed, or start point. This should fill one side of the edge. Remember the seed, and get centroid (and maybe area) of the new, filled in black area. Invert the filled-in image and get the centroid of the other part of the image.
You now know the centroids of both sides of the edge - as marked in red below:
Step 2:
Now move to the mask image. Maybe use dilation/erosion to fill in any smallish holes. Then run "labelling" on the image to get a list of the contiguous black blobs and their centroids and areas. Select the largest blob by area.
You should now have the centroid of the largest blob as marked in green below:
Step 3:
Now choose the nearer of the two red points to the green one and use the corresponding seed to do your fill.
It may better at Step 1 to repeatedly choose white seed points at random till you get a different centroid, rather than doing the inversion. That's because if you just invert and get the centroid, you don't know a good seed pixel. It's not certain that the centroid a good seed.
Seems like you need to find the Center of Mass (CoM) of the mask function to determine which side to fill, and then use floodFill to the edge image from the CoM as seeding point.
You could try something like this:
# Calculate the Center of Mass
com = np.zeros(2)
sum = np.zeros(2)
for x in range(0, nu_of_rows):
for y in range(0, num_of_cols):
com += image[x][y] * np.array([x,y])
sum += image[x][y]
com /= sum
# FloodFill a new image
h, w = mask_img.shape[:2]
new_image = mask_img.copy()
temp = np.zeros((h+2,w+2),np.uint8) # Needs to be 2 pixels wider/higher
cv2.FloodFill(new_image, temp, coi, 255, flags=cv2.FLOODFILL_MASK_ONLY)
This will work if the line in your edge and mask images has values 255 and the backgrounds 0. If this is not the case, first invert your two images with the following command:
inverted_img = cv2.bitwise_not(img)
Note: I did not test this with your image, but rather with one of mine. So you might have to change something here and there. Here is my rough example working:
I apologize in advance if a question like this was already answered. All of my searches for adding filters resulted in how to add dog faces. I wasn't sure what the proper terminology is.
What techniques do phone apps (such as Snapchat's text overlay or a QR code program for android) use to "darken" a section of the image? I am looking to replace this functionality in OpenCV. Is it possible to do this with other colors? (Such as adding a blue accent)
Example: Snapchat text overlay
https://i.imgur.com/9NHfiBY.jpg
Another Example: Google Allo QR code search
https://i.imgur.com/JnMzvWT.jpg
Any questions or comments would be appreciated
In General:
Change of brightness can be achieved via Addition/Subtraction.
If you want to brighten your Image, you can add a specific amount (e.g. 20) to each channel of the image. The other way around for darkening (Subtraction).
If you would subtract 50 from each channel of the image, you would get:
To darken pixel dependent you could also use Division. This is how a division with 1.5 would change the image:
Another way would be to use the Exponential Operator. This operator takes the value of each channel and will then calculate pixel^value. The resulting value will be then scaled back to the 0-255 range (for 8 bit RGB) via looking the highest value and then calculating the scaling factor via 255/resulting value.
If use it with values > one, it will darker the image. This is because
Here a chart how the exponential operator will change the value of each pixel. As you can see, values for the operator above 1 will darken the image (meaning the channels will be shifted towards lower values), whilst values below 0 will shift all pixels towards white and thus increase brightness.
Here is an example image for application of the operator using the value 0.5, meaning you take each pixel^0.5 and scale it back to the range of 0-255:
For a value of 2 you get:
Sadly i can not help you further, because i am not familiar with OpenCV, but it should be easy enough to implement yourself.
For your question about tinting: Yes, that is also possible. Instead of shifting towards white, you would have to shift the values of each pixel towards the respective color. I recommend to inform you about blending.
Original image taken from here
Update: I was able to darken an image by blending an image matrix with a black matrix. After that, it was just a matter of darkening certain parts of the image to replicate an overlay.
The lower the alpha value is, the darker the image.
Result
void ApplyFilter(cv::Mat &inFrame, cv::Mat &outFrame, double alpha)
{
cv::Mat black = cv::Mat(inFrame.rows, inFrame.cols, inFrame.type(), 0.0);
double beta = (1.0 - alpha);
cv::addWeighted(inFrame, alpha, black, beta, 0.0, outFrame);
}
https://docs.opencv.org/2.4/doc/tutorials/core/adding_images/adding_images.html
Thank you for the help everyone!
I'm trying to show students how the RGB color model works to create a particular color (or moreover to convince them that it really does). So I want to take a picture and convert each pixel to an RGB representation so that when you zoom in, instead of a single colored pixel, you see the RGB colors.
I've done this but for some very obvious reasons the converted picture is either washed out or darker than the original (which is a minor inconvenience but I think it would be more powerful if I could get it to be more like the original).
Here are two pictures "zoomed out":
Here is a "medium zoom", starting to show the RGB artifacts in the converted picture:
And here is a picture zoomed in to the point that you can clearly see individual pixels and the RGB squares:
You'll notice the constant color surrounding the pixels; that is the average RGB of the picture. I put that there so that you could see individual pixels (otherwise you just see rows/columns of shades of red/green/blue). If I take that space out completely, the image is even darker and if I replace it with white, then the image looks faded (when zoomed out).
I know why displaying this way causes it to be darker: a "pure red" will come with a completely black blue and green. In a sense if I were to take a completely red picture, it would essentially be 1/3 the brightness of the original.
So my question is:
1: Are there any tools available that already do this (or something similar)?
2: Any ideas on how to get the converted image closer to the original?
For the 2nd question, I could of course just increase the brightness for each "RGB pixel" (the three horizontal stripes in each square), but by how much? I certainly can't just multiply the RGB ints by 3 (in apparent compensation for what I said above). I wonder if there is some way to adjust my background color to compensate for me? Or would it just have to be something that needs to be fiddled with for each picture?
You were correct to assume you could retain the brightness by multiplying everything by 3. There's just one small problem: the RGB values in an image use gamma correction, so the intensity is not linear. You need to de-gamma the values, multiply, then gamma correct them again.
You also need to lose the borders around each pixel. Those borders take up 7/16 of the final image which is just too much to compensate for. I tried rotating every other pixel by 90 degrees, and while it gives the result a definite zig-zag pattern it does make clear where the pixel boundaries are.
When you zoom out in an image viewer you might see the gamma problem too. Many viewers don't bother to do gamma correction when they resize. For an in-depth explanation see Gamma error in picture scaling, and use the test image supplied at the end. It might be better to forgo scaling altogether and simply step back from the monitor.
Here's some Python code and a crop from the resulting image.
from PIL import Image
im = Image.open(filename)
im2 = Image.new('RGB', (im.size[0]*3, im.size[1]*3))
ld1 = im.load()
ld2 = im2.load()
for y in range(im.size[1]):
for x in range(im.size[0]):
rgb = ld1[x,y]
rgb = [(c/255)**2.2 for c in rgb]
rgb = [min(1.0,c*3) for c in rgb]
rgb = tuple(int(255*(c**(1/2.2))) for c in rgb)
x2 = x*3
y2 = y*3
if (x+y) & 1:
for x3 in range(x2, x2+3):
ld2[x3,y2] = (rgb[0],0,0)
ld2[x3,y2+1] = (0,rgb[1],0)
ld2[x3,y2+2] = (0,0,rgb[2])
else:
for y3 in range(y2, y2+3):
ld2[x2,y3] = (rgb[0],0,0)
ld2[x2+1,y3] = (0,rgb[1],0)
ld2[x2+2,y3] = (0,0,rgb[2])
Don't waste so much time on this. You cannot make two images look the same if you have less information in one of them. You still have your computer that will subsample your image in weird ways while zooming out.
Just pass a magnifying glass through the class so they can see for themselves on their phones or other screens or show pictures of a screen in different magnification levels.
If you want to stick to software, triple the resolution of your image, don't use empty rows and columns or at least make them black to increase contrast and scale the RGB components to full range.
Why don't you keep the magnified image for the background ? This will let the two images look identical when zoomed out, while the RGB strips will remain clearly visible in the zoom-in.
If not, use the average color over the whole image to keep a similar intensity, but the washing effect will remain.
An intermediate option is to apply a strong lowpass filter on the image to smoothen all details and use that as the background, but I don't see a real advantage over the first approach.
I am creating an animated gif with Water and Land. The Land part has shores, so the water will in some cases be overlaying those shores. However, I wish the shores to still be slightly visible underneath the water. Basically, imagine 1 transparent png which represents water, over another that has sand. The 2 images combined can be viewed as a third image.
My question is - how would I calculate the RGB value of the new pixels, based on the top ones RGBA (with transparency) and the bottom ones RGB values, to mimic a natural look.
I will change the transparency level myself to see what suits me best, but I'm missing a formula.
EDIT:
atm I do have 1 idea - take the percentage of the transparency level of the water, and based on that percentage calculate a new RGB. Do the same for the sand pixels, but with the "remaining" percentage (100-transparency%)
ie (A part is in %, for simplicity)
(R1,G1,B1, 40) (R2,G2,B2) = 40% of first group + 60% of second
Alpha blending is either dead simple (alpha is on 0-255, so the colour is destination * (255 - a) + overlay * a, all over 255) or quite subtle, when you allow the destination to also have alpha. Also it is necessary to implement efficiently for many applications, which is done by pre-multiplying, then hardcoding the division by 255 using
((x+1) * 257) >> 16;
Malcolm McLean gives the correct answer for the case where destination (aka background) is completely opaque, which happens to be the case we are discussing.
Just for completeness, I'd like you give the formula for the general case and the intuition behind it.
First of all, assume our colors are 4-component floating-point vectors, with each channel having values between 0 and 1. Also assume our colors are alpha-premultiplied, that is the r, g and b channels are pre-multiplied by the alpha channel. Then the alpha-blending formula is:
blended = front + back * (1.0 - front.a)
The intuition is: we are taking the front color and adding to it a bit of the back color, namely as much as we can see through the front color.
Note that the blended color is also alpha-premultiplied.
Given an image (Like the one given below) I need to convert it into a binary image (black and white pixels only). This sounds easy enough, and I have tried with two thresholding functions. The problem is I cant get the perfect edges using either of these functions. Any help would be greatly appreciated.
The filters I have tried are, the Euclidean distance in the RGB and HSV spaces.
Sample image:
Here it is after running an RGB threshold filter. (40% it more artefects after this)
Here it is after running an HSV threshold filter. (at 30% the paths become barely visible but clearly unusable because of the noise)
The code I am using is pretty straightforward. Change the input image to appropriate color spaces and check the Euclidean distance with the the black color.
sqrt(R*R + G*G + B*B)
since I am comparing with black (0, 0, 0)
Your problem appears to be the variation in lighting over the scanned image which suggests that a locally adaptive thresholding method would give you better results.
The Sauvola method calculates the value of a binarized pixel based on the mean and standard deviation of pixels in a window of the original image. This means that if an area of the image is generally darker (or lighter) the threshold will be adjusted for that area and (likely) give you fewer dark splotches or washed-out lines in the binarized image.
http://www.mediateam.oulu.fi/publications/pdf/24.p
I also found a method by Shafait et al. that implements the Sauvola method with greater time efficiency. The drawback is that you have to compute two integral images of the original, one at 8 bits per pixel and the other potentially at 64 bits per pixel, which might present a problem with memory constraints.
http://www.dfki.uni-kl.de/~shafait/papers/Shafait-efficient-binarization-SPIE08.pdf
I haven't tried either of these methods, but they do look promising. I found Java implementations of both with a cursory Google search.
Running an adaptive threshold over the V channel in the HSV color space should produce brilliant results. Best results would come with higher than 11x11 size window, don't forget to choose a negative value for the threshold.
Adaptive thresholding basically is:
if (Pixel value + constant > Average pixel value in the window around the pixel )
Pixel_Binary = 1;
else
Pixel_Binary = 0;
Due to the noise and the illumination variation you may need an adaptive local thresholding, thanks to Beaker for his answer too.
Therefore, I tried the following steps:
Convert it to grayscale.
Do the mean or the median local thresholding, I used 10 for the window size and 10 for the intercept constant and got this image (smaller values might also work):
Please refer to : http://homepages.inf.ed.ac.uk/rbf/HIPR2/adpthrsh.htm if you need more
information on this techniques.
To make sure the thresholding was working fine, I skeletonized it to see if there is a line break. This skeleton may be the one needed for further processing.
To get ride of the remaining noise you can just find the longest connected component in the skeletonized image.
Thank you.
You probably want to do this as a three-step operation.
use leveling, not just thresholding: Take the input and scale the intensities (gamma correct) with parameters that simply dull the mid tones, without removing the darks or the lights (your rgb threshold is too strong, for instance. you lost some of your lines).
edge-detect the resulting image using a small kernel convolution (5x5 for binary images should be more than enough). Use a simple [1 2 3 2 1 ; 2 3 4 3 2 ; 3 4 5 4 3 ; 2 3 4 3 2 ; 1 2 3 2 1] kernel (normalised)
threshold the resulting image. You should now have a much better binary image.
You could try a black top-hat transform. This involves substracting the Image from the closing of the Image. I used a structural element window size of 11 and a constant threshold of 0.1 (25.5 on for a 255 scale)
You should get something like:
Which you can then easily threshold:
Best of luck.