I am fairly new to Unity but am trying to take a photo from the camera and save it. Taking a screen capture is not an option. When I take the photo it appears clear in the center of the photo but gets further distorted towards the edges and I am not sure why. The main camera is linked to the realCamera object in Unity.
The screenshot code, which calls on update is:
DirectoryInfo screenshotDirectory = Directory.CreateDirectory(directoryName);
fileO = original + fileNameEnd + count.ToString() + fileType;
string fullPathO = Path.Combine(screenshotDirectory.FullName, fileO);
RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);
Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false);
byte[] bytes;
realCamera.targetTexture = rt;
realCamera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
realCamera.targetTexture = null;
RenderTexture.active = null;
Destroy(rt);
bytes = screenShot.EncodeToPNG();
System.IO.File.WriteAllBytes(fullPathO, bytes);
Related
I have got a quad that has got a target texture set through Unity Editor. I would like to save the output visualization on the quad as an image. Are there any ways to save it as a quad? I have tried through texture2d but its just a black image that is being saved.
Try this
private void SaveImage(Texture t, string path)
{
RenderTexture rt = new RenderTexture(t.width, t.height, 0);
Graphics.Blit(t, rt);
Texture2D t2d = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
t2d.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
File.WriteAllBytes(path, t2d.EncodeToPNG());
}
Usage
SaveImage(yourQuad.GetComponent<MeshRenderer>().material.mainTexture, "yourSavePath.png");
I have a gallery scene and I want to load PNG's from Persistence path .
The thing is that I want them as thumbnails, theirs no need for me to load the full size file .
How can I defined the scale of the sprite?
that the relevant line for the creating of the sprite:
Sprite sp1 = Sprite.Create(texture1, new Rect(0, 0, texture1.width, texture1.height), new Vector2(0.5f, 0.5f), 100, 0, SpriteMeshType.FullRect);
and that the texture creating code:
Texture2D takeScreenShotImage(string filePath)
{
Texture2D texture = null;
byte[] fileBytes;
if (File.Exists(filePath))
{
fileBytes = File.ReadAllBytes(filePath);
texture = new Texture2D(1, 1, TextureFormat.ETC2_RGB, false);
texture.LoadImage(fileBytes);
}
return texture;
}
The proper place to make that change is on the Texture2D after loading the Texture. If you really want to load them as thumbnails and want the size to be smaller to save memory then resize it with the Texture2D.Resize function after loading the Texture. The height and width of 60 should be fine. You can then create Sprite from it with Sprite.Create.
Texture2D takeScreenShotImage(string filePath)
{
Texture2D texture = null;
byte[] fileBytes;
if (File.Exists(filePath))
{
fileBytes = File.ReadAllBytes(filePath);
texture = new Texture2D(1, 1, TextureFormat.ETC2_RGB, false);
texture.LoadImage(fileBytes);
}
//RE-SIZE THE Texture2D
texture.Resize(60, 60);
texture.Apply();
return texture;
}
Note that TextureFormat.ETC2_RGB is compressed. To resize it you may have to create a copy of it. See #1 solution from my other post on how to do this.
Is it possible to crop the captured image based on the shape that I want? I'm using raw image + web cam texture to activate the camera and save the image. And I'm using UI Image overlay method as a mask to cover the unwanted parts. I will be attaching the picture to the char model in the latter part. Sorry, I am new to unity. Grateful for your help!
Below is what I have in my code:
// start cam
void Start () {
devices = WebCamTexture.devices;
background = GetComponent<RawImage> ();
devCam = new WebCamTexture ();
background.texture = devCam;
devCam.deviceName = devices [0].name;
devCam.Play ();
}
void OnGUI()
{
GUI.skin = skin;
//swap front and back camera
if (GUI.Button (new Rect ((Screen.width / 2) - 1200, Screen.height - 650, 250, 250),"", GUI.skin.GetStyle("btn1"))) {
devCam.Stop();
devCam.deviceName = (devCam.deviceName == devices[0].name) ? devices[1].name : devices[0].name;
devCam.Play();
}
//snap picture
if (GUI.Button (new Rect ((Screen.width / 2) - 1200, Screen.height - 350, 250, 250), "", GUI.skin.GetStyle ("btn2"))) {
OnSelectCapture ();
//freeze cam here?
}
}
public void OnSelectCapture()
{
imgID++;
string fileName = imgID.ToString () + ".png";
Texture2D snap = new Texture2D (devCam.width, devCam.height);
Color[] c;
c = devCam.GetPixels ();
snap.SetPixels (c);
snap.Apply ();
// Save created Texture2D (snap) into disk as .png
System.IO.File.WriteAllBytes (Application.persistentDataPath +"/"+ fileName, snap.EncodeToPNG ());
}
}
Unless I am not understanding your question correctly, you can just call `devCam.pause!
Update
What you're looking for is basically to copy the pixels from the screen onto a separate image under some condition. So you could use something like this: https://docs.unity3d.com/ScriptReference/Texture2D.EncodeToPNG.html
I'm not 100% sure what you want to do with it exactly but if you want to have an image that you can use as a sprite, for instance, you can scan each pixel and if the pixel colour value is the same as the blue background, swap it for a 100% transparent pixel (0 in the alpha channel). That will give you just the face with the black hair and the ears.
Update 2
The link that I referred you to copies all pixels from the camera view so you don't have to worry about your source image. Here is the untested method, it should work plug and play so long as there is only one background colour else you will need to modify slightly to test for different colours.
IEnumerator GetPNG()
{
// Create a texture the size of the screen, RGB24 format
yield return new WaitForEndOfFrame();
int width = Screen.width;
int height = Screen.height;
Texture2D tex = new Texture2D(width, height, TextureFormat.RGB24, false);
// Read screen contents into the texture
tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
tex.Apply();
//Create second texture to copy the first texture into minus the background colour. RGBA32 needed for Alpha channel
Texture2D CroppedTexture = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
Color BackGroundCol = Color.white;//This is your background colour/s
//Height of image in pixels
for(int y=0; y<tex.height; y++){
//Width of image in pixels
for(int x=0; x<tex.width; x++){
Color cPixelColour = tex.GetPixel(x,y);
if(cPixelColour != BackGroundCol){
CroppedTexture.SetPixel(x,y, cPixelColour);
}else{
CroppedTexture.SetPixel(x,y, Color.clear);
}
}
}
// Encode your cropped texture into PNG
byte[] bytes = CroppedTexture.EncodeToPNG();
Object.Destroy(CroppedTexture);
Object.Destroy(tex);
// For testing purposes, also write to a file in the project folder
File.WriteAllBytes(Application.dataPath + "/../CroppedImage.png", bytes);
}
I made a simple app for android devices that uses mobile's camera to take a photo and save it in internal storage folder /mnt/sdcard/DCIM/Camerizeman/
Photos are saved correctly but the problem that i am faceing is that i can't see the photos from mobile's gallery. I can see them correctly if i use a file manager or reboot me device. i am searchig 10 days now and the problem is that i have to refresh the gallery after saving the image.
I didn't found any working solution.
my code is bellow:
RenderTexture renderTex;
Texture2D screenshot;
Texture2D LoadScreenshot;
int width = Screen.width; // for Taking Picture
int height = Screen.height; // for Taking Picture
string fileName;
string myScreenshotLocation;
string screenShotName = "MyImage_AR_" + System.DateTime.Now.ToString("yyyy-MM-dd-HHmmss") + ".png";
public void Snapshot ()
{
StartCoroutine (CaptureScreen ());
}
public IEnumerator CaptureScreen ()
{
yield return null; // Wait till the last possible moment before screen rendering to hide the UI
//GameObject.FindGameObjectWithTag("Snapshoot").SetActive(false);
yield return new WaitForEndOfFrame (); // Wait for screen rendering to complete
if (Screen.orientation == ScreenOrientation.Portrait || Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
mainCamera = Camera.main.GetComponent<Camera> (); // for Taking Picture
renderTex = new RenderTexture (width, height, 24);
mainCamera.targetTexture = renderTex;
RenderTexture.active = renderTex;
mainCamera.Render ();
screenshot = new Texture2D (width, height, TextureFormat.RGB24, false);
screenshot.ReadPixels (new Rect (0, 0, width, height), 0, 0);
screenshot.Apply (); //false
RenderTexture.active = null;
mainCamera.targetTexture = null;
}
if (Screen.orientation == ScreenOrientation.LandscapeLeft || Screen.orientation == ScreenOrientation.LandscapeRight) {
mainCamera = Camera.main.GetComponent<Camera> (); // for Taking Picture
renderTex = new RenderTexture (height, width, 24);
mainCamera.targetTexture = renderTex;
RenderTexture.active = renderTex;
mainCamera.Render ();
screenshot = new Texture2D (height, width, TextureFormat.RGB24, false);
screenshot.ReadPixels (new Rect (0, 0, height, width), 0, 0);
screenshot.Apply (); //false
RenderTexture.active = null;
mainCamera.targetTexture = null;
}
myScreenshotLocation = myFolderLocation + screenShotName;
File.WriteAllBytes (myFolderLocation + screenShotName, screenshot.EncodeToPNG ());
}
Please help!
The second working solution is:
using (AndroidJavaClass jcUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (AndroidJavaObject joActivity = jcUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
using (AndroidJavaObject joContext = joActivity.Call<AndroidJavaObject>("getApplicationContext"))
using (AndroidJavaClass jcMediaScannerConnection = new AndroidJavaClass("android.media.MediaScannerConnection"))
using (AndroidJavaClass jcEnvironment = new AndroidJavaClass("android.os.Environment"))
using (AndroidJavaObject joExDir = jcEnvironment.CallStatic<AndroidJavaObject>("getExternalStorageDirectory"))
{
jcMediaScannerConnection.CallStatic("scanFile", joContext, new string[] { YOURFULL IMAGE PATH}, null, null);
}
Use MediaScannerConnection. You need the image "scanned into" the gallery.
Native Java Code goes something like:
MediaScannerConnection.scanFile(unityPlayerActivity, new String[]{externalImagePath}, null, null);
May Create plugin - Unity Android plugin tutorial (1/3) Fundamentals -, or use AndroidJavaClass.CallStatic to invoke a MediaScannerConnection.scanFile.
I have in my assets a folder resources and inside an image pig.png, I want to create a sprite from code with this image, Here is my code:
var filePath = Application.dataPath + "/Resources/pig.png";
if (System.IO.File.Exists(filePath))
{
var bytes = System.IO.File.ReadAllBytes(filePath);
Texture2D tex = new Texture2D(1, 1);
tex.LoadImage(bytes);
Sprite sp = new Sprite();
sp = Sprite.Create(tex, new Rect(0, 0, 100, 100), new Vector2(0.5f, 0.5f),40);
}
The code runs when I click a button inside a gui, What's wrong?
Fixed code:
Texture2D tex = Resources.Load<Texture2D>("pig") as Texture2D;
Sprite sprite = new Sprite();
sprite = Sprite.Create(tex, new Rect(0, 0, 250, 150), new Vector2(0.5f, 0.5f));
GameObject newSprite = new GameObject();
newSprite.AddComponent<SpriteRenderer>();
SpriteRenderer SR = newSprite.GetComponent<SpriteRenderer>();
SR.sprite = sprite;
If your pig image is in the Assets/Resources folder, then you have to load the image with Resources.Load function not with File.ReadAllBytes.
//Assign Sprite from Editor. (Where to display the loaded image sprite)
public Image displaySprite;
void loadImage()
{
//Load Image
Texture2D tex = Resources.Load("pig", typeof(Texture2D)) as Texture2D;
if (tex != null)
{
//Create new Sprite from the Loaded Sprite
Sprite sp = new Sprite();
sp = Sprite.Create(tex, new Rect(0, 0, 100, 100), new Vector2(0.5f, 0.5f), 40);
Debug.Log("Not Null");
//Show Image to screen
displaySprite.sprite = sp;
}
else
{
Debug.Log("Null");
}
}