In my project, a background picture is fetched from cloud. But some picture is too light, and some are dark. So I need to detect the background picture's brightness and hence to decide whether to add a mask.
I searched the solution on Google and found most solution is for WPF and using Bitmap. But they are forbidden in UWP.
Like below code. When I run the UWP project, VS will report error:
System.PlatformNotSupportedException
HResult=0x80131539
Message=System.Drawing is not supported on this platform.
So how to detect it in UWP?
private static float GetBrightness(string url)
{
Bitmap bitmap = new Bitmap(url);
var colors = new List<Color>();
for (int x = 0; x < bitmap.Size.Width; x++)
{
for (int y = 0; y < bitmap.Size.Height; y++)
{
colors.Add(bitmap.GetPixel(x, y));
}
}
float imageBrightness = colors.Average(color => color.GetBrightness());
return imageBrightness;
}
This works:
private async Task<float> GetBrightness(string url)
{
WebRequest myrequest = WebRequest.Create(url);
WebResponse myresponse = myrequest.GetResponse();
var imgstream = myresponse.GetResponseStream();
// Try to create SoftwareBitmap
MemoryStream ms = new MemoryStream();
imgstream.CopyTo(ms);
var dec = await BitmapDecoder.CreateAsync(ms.AsRandomAccessStream());
var data = await dec.GetPixelDataAsync();
var bytes = data.DetachPixelData();
var colors = new List<Color>();
for (int x = 0; x < dec.PixelWidth; x++)
{
for (int y = 0; y < dec.PixelHeight; y++)
{
colors.Add(GetPixel(bytes, x, y, dec.PixelWidth, dec.PixelHeight));
}
}
float imageBrightness = colors.Average(color => color.GetBrightness());
return imageBrightness;
}
public Color GetPixel(byte[] pixels, int x, int y, uint width, uint height)
{
int i = x;
int j = y;
int k = (i * (int)width + j) * 3;
var r = pixels[k + 0];
var g = pixels[k + 1];
var b = pixels[k + 2];
return Color.FromArgb(0, r, g, b);
}
Source1, Source2
Related
How do I call the method FlipTextureVertically in MakePhoto ?
My picture currently taken is upside down in unity, and I came across this texture flipping code, but I do not know how to apply it.
Would really appreciate if someone could help me out here!
public static Texture2D FlipTextureVertically(Texture2D original)
{
Texture2D flipped = new Texture2D(original.width, original.height, TextureFormat.ARGB32, false);
int xN = original.width;
int yN = original.height;
for (int i = 0; i < xN; i++)
{
for (int j = 0; j < yN; j++)
{
flipped.SetPixel(i, yN - j - 1, original.GetPixel(i, j));
}
}
flipped.Apply();
return flipped;
}
public string MakePhoto(bool openIt)
{
int resWidth = Screen.width;
int resHeight = Screen.height;
Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false); //Create new texture
RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);
// hide the info-text, if any
if (infoText)
{
infoText.text = string.Empty;
}
// render background and foreground cameras
if (backroundCamera && backroundCamera.enabled)
{
backroundCamera.targetTexture = rt;
backroundCamera.Render();
backroundCamera.targetTexture = null;
}
if (backroundCamera2 && backroundCamera2.enabled)
{
backroundCamera2.targetTexture = rt;
backroundCamera2.Render();
backroundCamera2.targetTexture = null;
}
if (foreroundCamera && foreroundCamera.enabled)
{
foreroundCamera.targetTexture = rt;
foreroundCamera.Render();
foreroundCamera.targetTexture = null;
}
// get the screenshot
RenderTexture prevActiveTex = RenderTexture.active;
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
// clean-up
RenderTexture.active = prevActiveTex;
Destroy(rt);
byte[] btScreenShot = screenShot.EncodeToJPG();
Destroy(screenShot);
// save the screenshot as jpeg file
string sDirName = Application.persistentDataPath + "/Screenshots";
if (!Directory.Exists(sDirName))
Directory.CreateDirectory (sDirName);
string sFileName = sDirName + "/" + string.Format ("{0:F0}", Time.realtimeSinceStartup * 10f) + ".jpg";
File.WriteAllBytes(sFileName, btScreenShot);
Debug.Log("Photo saved to: " + sFileName);
if (infoText)
{
infoText.text = "Saved to: " + sFileName;
}
// open file
if(openIt)
{
System.Diagnostics.Process.Start(sFileName);
}
return sFileName;
}
I don't really see why the screenshot should be upside down but I guess you should call it e.g. after
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
screenShot = FlipTextureVertically(screenShot);
but there might be more efficient ways of doing that.
E.g. not creating a new Texture2D but instead alter only the pixels in the one you already have like
public static void FlipTextureVertically(Texture2D original)
{
var originalPixels = original.GetPixels();
var newPixels = new Color[originalPixels.Length];
var width = original.width;
var rows = original.height;
for (var x = 0; x < width; x++)
{
for (var y = 0; y < rows; y++)
{
newPixels[x + y * width] = originalPixels[x + (rows - y -1) * width];
}
}
original.SetPixels(newPixels);
original.Apply();
}
public static void FlipTextureHorizontally(Texture2D original)
{
var originalPixels = original.GetPixels();
var newPixels = new Color[originalPixels.Length];
var width = original.width;
var rows = original.height;
for (var x = 0; x < width; x++)
{
for (var y = 0; y < rows; y++)
{
newPixels[x + y * width] = originalPixels[(width - x - 1) + y * width];
}
}
original.SetPixels(newPixels);
original.Apply();
}
and use it like
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
FlipTextureVertically(screenShot);
The reason your image is flipped is that you are swicthing the vertical pixels in your code.
public static Texture2D FlipTextureVertically(Texture2D original)
{
Texture2D flipped = new Texture2D(original.width, original.height, TextureFormat.ARGB32, false);
int xN = original.width;
int yN = original.height;
for (int i = 0; i < xN; i++)
{
for (int j = 0; j < yN; j++)
{
flipped.SetPixel(i, yN - j - 1, original.GetPixel(i, j));
}
}
flipped.Apply();
return flipped;
}
should be
public static Texture2D FlipTextureVertically(Texture2D original)
{
Texture2D flipped = new Texture2D(original.width, original.height, TextureFormat.ARGB32, false);
int xN = original.width;
int yN = original.height;
for (int i = 0; i < xN; i++)
{
for (int j = 0; j < yN; j++)
{
flipped.SetPixel(xN - i - 1, yN, original.GetPixel(i, j));
}
}
flipped.Apply();
return flipped;
}
I used LibTiff.net to crop part of a tiled Tiff and export it as a tiled Tiff but encountered the problem of "can not write tiles image to a stripped image". While the "Tiff.open("out.tif","w") make a stripped image, How can I create a tiled-Tiff to fill it with input data?
using (Tiff input = Tiff.Open(#"E:\Sample_04.tif", "r"))
{
int width = input.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
int height = input.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
int tileWidth = input.GetField(TiffTag.TILEWIDTH)[0].ToInt();
int tileLentgh = input.GetField(TiffTag.TILELENGTH)[0].ToInt();
int samplesPerPixel = input.GetField(TiffTag.SAMPLESPERPIXEL)[0].ToInt();
int bitsPerSample = input.GetField(TiffTag.BITSPERSAMPLE)[0].ToInt();
int photo = input.GetField(TiffTag.PHOTOMETRIC)[0].ToInt();
int config = input.GetField(TiffTag.PLANARCONFIG)[0].ToInt();
int tiles = 0;
int tileSize = input.TileSize();
byte[][] buffer = new byte[tileSize][];
int tileHeightCount = height / tileLentgh;
int tileWidthCount = width / tileWidth;
for (int y = 0; y < tileLentgh*5; y += tileLentgh)
{
for (int x = 0; x < tileWidth*5; x += tileWidth)
{
buffer[tiles] = new byte[tileSize];
input.ReadTile(buffer[tiles], 0, x, y, 0, 0);
tiles++;
}
}
// writing
using (Tiff output = Tiff.Open("out.tif", "w"))
{
output.SetField(TiffTag.SAMPLESPERPIXEL, samplesPerPixel);
output.SetField(TiffTag.IMAGEWIDTH, width );
output.SetField(TiffTag.IMAGELENGTH, height);
output.SetField(TiffTag.BITSPERSAMPLE, bitsPerSample);
output.SetField(TiffTag.ROWSPERSTRIP, output.DefaultStripSize(0));
output.SetField(TiffTag.PHOTOMETRIC, photo);
output.SetField(TiffTag.PLANARCONFIG, config);
int c = 0;
for (int y = 0; y < tileLentgh*5; y += tileLentgh)
{
for (int x = 0; x < tileWidth*5; x += tileWidth)
{
output.WriteTile(buffer[c], x, y, 0, 0);
c++;
}
}
}
}
System.Diagnostics.Process.Start("555.tif");
}
To create a tiled TIFF please don't use
output.SetField(TiffTag.ROWSPERSTRIP, output.DefaultStripSize(0));
And you absolutely must set TiffTag.TILEWIDTH and TiffTag.TILELENGTH fields before using WriteTile method.
I'm having some trouble reading back pixel values from a Bitmap that I'm generating. I first generate a bitmap named maskBitmap in my class using this code:
void generateMaskBitmap()
{
if (inputBitmap != null)
{
Bitmap tempBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(tempBitmap))
{
Brush brush = Brushes.Black;
for (int y = 0; y < tempBitmap.Height; y += circleSpacing)
{
for (int x = 0; x < tempBitmap.Width; x += circleSpacing)
{
g.FillEllipse(brush, x, y, circleDiameter, circleDiameter);
}
}
g.Flush();
}
maskBitmap = (Bitmap)tempBitmap.Clone();
}
}
I then try to apply the mask to my original image using the following code:
void generateOutputBitmap()
{
if (inputBitmap != null && maskBitmap != null)
{
Bitmap tempBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height);
for (int y = 0; y < tempBitmap.Height; y++)
{
for (int x = 0; x < tempBitmap.Width; x++)
{
Color tempColor = maskBitmap.GetPixel(x, y);
if (tempColor == Color.Black)
{
tempBitmap.SetPixel(x, y, inputBitmap.GetPixel(x, y));
}
else
{
tempBitmap.SetPixel(x, y, Color.White);
}
}
}
outputBitmap = tempBitmap;
}
}
The mask bitmap is successfully generated and visible in a picture box, however the color values for every pixel when testing "tempColor" show empty (A = 0, R = 0, G = 0, B = 0). I am aware of the performance problems with getpixel/setpixel, but this is not an issue for this project. I'm also aware that "tempColor == Color.Black" is not a valid test, but that is just a place holder for my comparison code.
I am unable to reproduce your problem. I copy-and-pasted your code and made some modifications to make it work for me. I am able to confirm that tempColor is sometimes #FF000000.
I suspect you mixed up your bitmap references somewhere. Are you really sure you are never getting any value other than #00000000? Do your circleDiameter and circleSpacing have sensible values? And most importantly: are you absolutely sure that you are reading from the correct bitmap?
Here's my version of your code, which I know works:
using System;
using System.Drawing;
namespace Test
{
class Program
{
static void Main()
{
var bitmap = GenerateMaskBitmap(100, 100);
TestMaskBitmap(bitmap);
}
const int CircleDiameter = 10;
const int CircleSpacing = 10;
static Bitmap GenerateMaskBitmap(int width, int height)
{
Bitmap maskBitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(maskBitmap))
{
Brush brush = Brushes.Black;
for (int y = 0; y < maskBitmap.Height; y += CircleSpacing)
{
for (int x = 0; x < maskBitmap.Width; x += CircleSpacing)
{
g.FillEllipse(brush, x, y, CircleDiameter, CircleDiameter);
}
}
g.Flush();
}
return maskBitmap;
}
static void TestMaskBitmap(Bitmap maskBitmap)
{
for (int y = 0; y < maskBitmap.Height; y++)
{
for (int x = 0; x < maskBitmap.Width; x++)
{
Color tempColor = maskBitmap.GetPixel(x, y);
if (tempColor.ToArgb() != 0)
throw new Exception("It works!");
}
}
}
}
}
I am just experimenting with Silverlight however I've run into an issue with a Canvas element. I am trying to generate a grid of rectangle based off a multidimensional array but when I run the code, the canvas has no elements. I wonder if you could offer some guidance for this?
public void generate(Rectangle[, ,] worldData)
{
int locx = 0, locy = 0;
Rectangle currentBlock = new Rectangle();
int z = 0;
for (int x = 0; x < worldData.GetLength(1); x++)
{
for (int y = 0; y < worldData.GetLength(2); y++)
{
currentBlock = worldData[z, x, y];
Canvas.SetTop(currentBlock, locy);
Canvas.SetLeft(currentBlock, locx);
locy = locy + 32;
gridDisplayCanvas.Children.Add(currentBlock);
}
locx = locx + 32;
locy = 0;
}
}
I figured out what is causing the issue, the images where being referenced with a \ but silverlight expects a \
I'm looking for sample .NET code (System.Drawing.Image) that does the following:
Load a given image file.
Generate a new single image that repeats the orginal image for x times horizontally.
This creates a new bitmap and draws the source bitmap to it numTimes times.
Bitmap b = Bitmap.FromFile(sourceFilename);
Bitmap output = new Bitmap(b.Width * numTimes, b.Height);
Graphics g = Graphics.FromImage(output);
for (int i = 0; i < numTimes; i++) {
g.DrawImage(b, i * b.Width, 0);
}
// do whatever with the image, here we'll output it
output.Save(outputFilename);
// make sure to clean up too
g.Dispose();
b.Dispose();
output.Dispose();
Here a sample copying each source image pixel on destination bitmap
static void Main(string[] args)
{
ushort nbCopies = 2;
Bitmap srcBitmap = (Bitmap)Image.FromFile(#"C:\Users\Public\Pictures\Sample Pictures\Koala.jpg");
Bitmap dstBitmap = new Bitmap(srcBitmap.Width * nbCopies, srcBitmap.Height, srcBitmap.PixelFormat);
//Slow method
for (int curCopy = 0; curCopy < nbCopies; curCopy++)
{
for (int x = 0; x < srcBitmap.Width; x++)
{
for (int y = 0; y < srcBitmap.Height; y++)
{
Color c = srcBitmap.GetPixel(x, y);
dstBitmap.SetPixel(x + (curCopy * srcBitmap.Width), y, c);
}
}
}
//OR
//Fast method using unsafe code
BitmapData srcBd = srcBitmap.LockBits(new Rectangle(Point.Empty, srcBitmap.Size), ImageLockMode.ReadOnly, srcBitmap.PixelFormat);
BitmapData dstBd = dstBitmap.LockBits(new Rectangle(Point.Empty, dstBitmap.Size), ImageLockMode.WriteOnly, dstBitmap.PixelFormat);
unsafe
{
for (int curCopy = 0; curCopy < nbCopies; curCopy++)
{
for (int y = 0; y < srcBitmap.Height; y++)
{
byte* srcRow = (byte*)srcBd.Scan0 + (y * srcBd.Stride);
byte* dstRow = (byte*)dstBd.Scan0 + (y * dstBd.Stride) + (curCopy * srcBd.Stride);
for (int x = 0; x < srcBitmap.Width; x++)
{
//Copy each composant value (typically RGB)
for (int comp = 0; comp < (srcBd.Stride / srcBd.Width); comp++)
{
dstRow[x * 3 + comp] = srcRow[x * 3 + comp];
}
}
}
}
}
dstBitmap.UnlockBits(dstBd);
srcBitmap.UnlockBits(srcBd);
dstBitmap.Save(#"C:\Users\Public\Pictures\Sample Pictures\Koala_multiple.jpg");
dstBitmap.Dispose();
srcBitmap.Dispose();
}