Loading an image as Texture2D - c#

In my program, I want to save some screenshots and load them later on to compute somethings. I created a methode to compute the image names:
static public string generatePhotoName (string cameraName, float time)
{
return "G:/Data/unity/cameraDemo/" +
DataController.measurmentPath +
"/photos" + "/" +
cameraName + "/" +
time.ToString () + "_" + cameraName + ".png";
}
This worked fine for saving, but when I try to load an image, File.Exists (filePath)returns false.
But when I hardcoded the filepath, loading works fine too:
static public string generatePhotoName (string cameraName, float time)
{
return "G:/Data/unity/cameraDemo/demo/photos/Camera/test.png";
}
It even works with "real" image names(i.e. 3.827817_Camera.png).
Using Path.Combine(...) and changing "/" to "\" did not change anything...
// edit: this is my load methode
static public Texture2D loadPhotoToTexture (string filePath)
{
Texture2D tex = null;
byte[] fileData;
if (File.Exists (filePath)) {
fileData = File.ReadAllBytes (filePath);
tex = new Texture2D (2, 2);
tex.LoadImage (fileData); //..this will auto-resize the texture dimensions.
} else {
Debug.Log (filePath + " does not exist");
}
return tex;
}`
// edit2: some more code
This is how I call the methode
Texture2D photo = DataController.loadPhotoToTexture(photoData.getFileName ());
And this is my class PhotoData
public class PhotoData : BaseData
{
private string _cameraName;
public string cameraName {
get { return _cameraName; }
set { _cameraName = value; }
}
private float _time;
public float time {
get { return _time; }
set { _time = value; }
}
public PhotoData ()
{
}
public PhotoData (string cameraName, float time)
{
_cameraName = cameraName;
_time = time;
}
public string getFileName ()
{
return PlayerController.generatePhotoName (_cameraName, _time);
}
}

The problem was, that I tried to save and load the screenshots in one Update()-call.
I fixed it by changing it to right-click to take and save a screenshot and left-click to load the screenshot.

If you want to store images to a certain file on disk, not inside your game folder structure, do this: create a folder, and store its location (filepath). For example:
string screenshotFileName = time.ToString () + "_" + cameraName + ".png"
string screenshotDirectory;
screenshotDirectory = #"C:\someFolder\anotherFolder\";
try { Directory.CreateDirectory(directory); }
catch (Exception e)
{ //you should catch each exception separately
if (e is DirectoryNotFoundException || e is UnauthorizedAccessException)
Debug.LogError("OMG PANIC");
}
FileInfo filepath = new FileInfo(screenshotDirectory+screenshotFileName);
if(filepath.Exists)
{
//load the file
}
You could also simply create a folder, with a relative path, which will be in the same folder as your executable, by changing the screenshotDirectory to
screenshotDirectory = #"screenshots\"+cameraName+#"\";
Edit:
You seem to load the texture correctly. Are you assigning it to the material's main texture? Where is your code encountering the problem? For example if you're running this script on the same object that you want the texture to be applied:
this.GetComponent<Renderer>().material.mainTexture = loadedTexture;
Also, when you want to load images, it's best that you use the Resources folder, which uses forwards slashes, not . Store everything that you might want to load on runtime there, and use Resources.Load().

Related

Try Catch Statement just not catching exception

I have a simple thumbnail function that I wrote for a program that displays pictures and videos in a folder. If it is a video I want to generate a picture to display. Now it is buggy to do this in folders with tons of files so I put it in a try catch statement and just said first try to get the thumbnail, if that doesn't work get the icon, and if that doesn't work just don't display it. Works fine when I debug but when I hammer it with files in production it occasionally crashes. Not surprising since it runs much faster in production. But clearly, since I only have this shellfile function in only one place, the try catch is not catching the exception.
public class Picture : INotifyPropertyChanged
{
private string _path;
private string _thumbpath;
public string ext { get; set; }
public string Thumbpath
{
get
{
if (_thumbpath != "MOVIE!")
{
return _thumbpath;
}
else
{
try
{
Microsoft.WindowsAPICodePack.Shell.ShellFile shellFile = Microsoft.WindowsAPICodePack.Shell.ShellFile.FromFilePath(_path);
System.Drawing.Bitmap bm = shellFile.Thumbnail.Bitmap;
string filename = "RECTHUMB" + RandomString(7) + ".jpg";
bm.Save(temppath + filename, System.Drawing.Imaging.ImageFormat.Jpeg);
bm.Dispose();
return temppath + filename;
}
catch
{
try
{
System.Drawing.Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(_path);
System.Drawing.Bitmap bm = icon.ToBitmap();
string filename = "RECTHUMB" + RandomString(7) + ".jpg";
bm.Save(temppath + filename, System.Drawing.Imaging.ImageFormat.Jpeg);
//bm.Dispose();
return temppath + filename;
}
catch
{
return "";
}
}
}
}
set
{
_thumbpath = value;
}
}
}
Here is the event log:
<EventData><Data>Application: Meta_Data_Mapper.exe CoreCLR Version:
7.0.222.60605 .NET Version: 7.0.2 Description: The process was terminated due to an unhandled exception. Stack: at
Microsoft.WindowsAPICodePack.Shell.IShellItemImageFactory.GetImage(Size,
SIIGBF, IntPtr ByRef) at
Microsoft.WindowsAPICodePack.Shell.ShellThumbnail.GetHBitmap(System.Windows.Size)
at Meta_Data_Mapper.MainWindow+Picture.get_Thumbpath() at ...

Serialization exception for vector3 when no vector3 is mentioned when trying to save variables

I'm building off of Sebastian Lague's videos on procedural planets, and I stopped where I was happy with what the scripts did. So I worked on procedurally generating a system with the planets based off of presets.
I got to saving these systems with AssetDatabase.Save and Load and when it didnt work to build I moved to formatting it to save the data. I used Binary formatting because it seemed simple and the easiest to find tutorials on how to do.
However when I attempt to save data I get
SerializationException: Type 'UnityEngine.Vector3' in Assembly 'UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
here's the scripts that cause the errors
Save script
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
[System.Serializable]
public class Vector3Ser // serializealbe Vector3
{
public float x, y, z;
}
[System.Serializable]
public static class SaveSystem
{
const string savesPath = "/Saves";
public static void Save<t>(t obj, string key)
{
BinaryFormatter bf = new BinaryFormatter();
string path = Application.persistentDataPath + savesPath;
Directory.CreateDirectory(path); // create path if it doesnt exist
FileStream stream = new FileStream(path + key, FileMode.Create);
bf.Serialize(stream, obj); // save obj
stream.Close();
}
public static void CreateFolder(string key)
{
string path = Application.persistentDataPath + savesPath;
Directory.CreateDirectory(path + key); // create folder at path
}
public static T Load<T>(string key)
{
BinaryFormatter bf = new BinaryFormatter();
string path = Application.persistentDataPath + savesPath;
T data = default;
if (File.Exists(path + key))
{
FileStream stream = new FileStream(path + key, FileMode.Open);
stream.Flush(); //no idea why its there but I found it somewhere and tried it
stream.Position = 0; //no idea why its there but I found it somewhere and tried it
data = (T)bf.Deserialize(stream); // save the deserialized file to a variable
stream.Close();
} else
{
Debug.LogError("Failed to load save at " + path + key);
}
return data; // return file/data
}
public static bool SaveExists(string key)
{
string path = Application.persistentDataPath + savesPath;
return File.Exists(path + key);
}
}
Data class that I'm attempting to save
[System.Serializable]
public class PlanetData
{
public string planetName;
public Vector3Ser planetPos;
public int planetID = -1;
public float planetRadius;
public bool[] noiseLayerEnabled;
public bool[] nLFirstLayerAsMask;
public NoiseSettings[] noiseLayerNoiseSettings;
public string indentifier;
public PlanetData(ShapeSettings sS, ColourSettings cS)
{
planetName = sS.name + "_" + cS.name; // load name
planetRadius = sS.planetRadius; // load radius and planet id
planetID = sS.planetID;
planetPos = new Vector3Ser(); // load planet pos
planetPos.x = sS.PlanetPos.x; // load planet pos values
planetPos.y = sS.PlanetPos.y;
planetPos.z = sS.PlanetPos.z;
noiseLayerEnabled = new bool[sS.noiseLayers.Length]; // initalize these values
nLFirstLayerAsMask = new bool[sS.noiseLayers.Length];
noiseLayerNoiseSettings = new NoiseSettings[sS.noiseLayers.Length];
for (int i = 0; i < sS.noiseLayers.Length; i++) // loop through all values in sS.noiselayers
{
noiseLayerEnabled[i] = sS.noiseLayers[i].enabled;
nLFirstLayerAsMask[i] = sS.noiseLayers[i].useFirstLayerAsAMask;
noiseLayerNoiseSettings[i] = sS.noiseLayers[i].noiseSettings;
}
indentifier = cS.indentifier;
}
}
script snipits used for saving
if (SaveSystem.SaveExists(universeKey))//check if it exists
{
universeSettings = SaveSystem.Load<UniverseSettings>(universeKey); // if it does load it
} else
{
universeSettings = new UniverseSettings(); // otherwise make a new instance
}
------------
string key = "/GeneratedScenes/" + SceneManager.GetActiveScene().name + "/" + "PlanetData_" + i + ".gameDat"; // Save location
planetData = new PlanetData(BarrenShapePrefab, BarrenColourPrefab);
if (SaveSystem.SaveExists(key)) //check if it exists
{
planetData = SaveSystem.Load<PlanetData>(key); // if it does load it
}
if (!SaveSystem.SaveExists(key)) // otherwise make a new instance
{
SaveSystem.Save(planetData, key);
}
if (universeSettings.planetNum != numOfPlanets) // check if the amount of planets is saved (other bits of code will set the num of planets to this value if loaded)
{
string universeKey = "/GeneratedScenes/" + SceneManager.GetActiveScene().name + "/" + "UniverseData" + ".gameDat";
universeSettings.planetNum = numOfPlanets; // set value
SaveSystem.Save(universeSettings, universeKey); // save values
}

(Updated) Working with files, check if exists or not

I am working with files on C# and I got to a point where I don't know how to continue anymore.
The scenario is this: If I upload 3 or more files with the same name at the same time, I want to handle them and change their name to from "myfile.pdf" to "myfile.pdf(1)/(2)/(3)..." depending on how much files I upload.
This is what I have tried so far and in this case, this only works for only the second file because when the third one comes, it will check there is any file with the same - yes, okay name it "myfile.pdf(2) - but this exists too so it will go to another place.
How can I achieve having the same three files in the same folder with this naming convention?
Here's what I have tried so far:
string FileName = "MyFile.pdf";
string path = #"C:\Project\MyPdfFiles\"
if (File.Exists(path))
{
int i = 1;
var FileExists = false;
while (FileExists==false)
{
if (FileExists == false)
{
FileName = FileName + "(" + i + ")";
}
else
return;
i++;
}
}
And the result of this code is: "MyFile.pdf", "MyFile.pdf(1)" And the third one doesn't load here.
I think I'm missing something in the loop or idk :(.
Can someone help me?
I have tried also this:
if(File.Exists(path) || File.Exists(path+"(")
//because when the second file its uploaded, its name will be SecondFile.pdf(1), so this will return true and will proceed running, but still the iteration will "always" start from 0 since everytime I upload a file, I have to refresh the process.
Don't use return inside your while loop, better set 'FileExists = true' whenever you want you loop to stop. A return statement will exit your current method.
I think your problem can be easily solved using recursion, something like this (untested):
public class Program
{
public string FileName { get; set; }
public Program() {
string fileName = "MyFile.pdf";
string path = #"C:\Project\MyPdfFiles\";
FileName = CheckFileName(path, fileName);
}
public string CheckFileName(string path, string fileName, int iteration = 0) {
if (File.Exists($"{path}{fileName}")) {
iteration++;
CheckFileName(path, $"{fileName}({iteration})", iteration);
}
return fileName;
}
}
What this does is: it CheckFileName method will keep calling itself until it finds a name that doesn't exist yet.
This should do the job.
public class Program
{
public static string GetUnusedFilePath(string directorypath, string filename, string ext)
{
string fullPath = $"{directorypath}{filename}{ext}";
int inc = 0;
// check until you have a filepath that doesn't exist
while (File.Exists(fullPath))
{
fullPath = $"{directorypath}{filename}{inc}{ext}";
inc++;
}
return fullPath;
}
public static void UploadFile(string filepath)
{
using (FileStream fs = File.Create(filepath))
{
// Add some text to file
Byte[] title = new UTF8Encoding(true).GetBytes("New Text File");
fs.Write(title, 0, title.Length);
}
}
public static void Main()
{
string[] filestoUpload = { "file", "file", "file", "anotherfile", "anotherfile", "anotherfile" };
string directorypath = #"D:\temp\";
string ext = ".txt";
foreach(var file in filestoUpload)
{
var filePath = GetUnusedFilePath(directorypath, file, ext);
UploadFile(filePath);
}
}
}
I solved this by creating new folders with special names using the code below:
DirectoryInfo hdDirectoryInWhichToSearch = new DirectoryInfo(FileDirectory);
FileSystemInfo[] filesAndDirs = hdDirectoryInWhichToSearch.GetFileSystemInfos("*" + FullFileName + "*");
int i = filesAndDirs.Length;
if (i>1)
{
FileName = Filename + "(" + i ")";
}
So what this does is that it will count how many files we have in that folder with the same name, so I have to check if we have more than 1 file, then change it's name to file(1).
Thank you to everyone that tried to help me, much appreciated.

Access Images and videos from android internal Storage using Unity C#

i am trying to access the Whatsapp folder from android internal storage but for some reason this code is not working the file path is correct but this if statement never gets true
here is the code for checking if file exixts
public static Texture2D LoadPNG()
{
Texture2D tex = null;
byte[] fileData;
if (File.Exists(filePath))
{
Debug.Log("file exists");
fileData = File.ReadAllBytes(filePath);
tex = new Texture2D(2, 2);
tex.LoadImage(fileData); //..this will auto-resize the texture dimensions.
}
return tex;
}
now filepath = "/storage/emulated/0/Whatsapp/Media/.statuses
code which makes this path
public static string GetDownloadFolder()
{
string[] temp = (Application.persistentDataPath.Replace("Android", "")).Split(new string[] { "//" }, System.StringSplitOptions.None);
filePath = temp[0]+"/WhatsApp/Media/.Statuses";
return (temp[0] + "/WhatsApp/Media/.Statuses");
}
now problem is that in android device this .statuses folder have images and video files but this if statement never gets called why i dont understand even path is correct
if (File.Exists(filePath))
{
Debug.Log("file exists");
fileData = File.ReadAllBytes(filePath);
tex = new Texture2D(2, 2);
tex.LoadImage(fileData); //..this will auto-resize the texture dimensions.
}
Your problem is that you check if file exists at folder path.
The solution is check if path is directory and get all files from it:
if (Directory.Exists(path))
{
string filenames[] = Directory.GetFiles(path);
foreach (var filename in filenames)
{
LoadPNG(filename);
}
}

ExternalException: A generic error occurred in GDI+

I am using Selenium WebDriver in C# and
I am trying to dynamically create a folder and save screenshots of failing tests to it.
Here I am running the group of test cases (Test Suite of 66 test cases).
After running the test suite I found few failed tests with GDI+ error and were not captured as a screenshot.
But when I run them individually most of the failed cases (GDI+ error) were passing except few.
Here is the code for creating a folder:
TestExecutionStartTime = DateTime.Now;
baseDirectory = AppDomain.CurrentDomain.BaseDirectory + #"\" + ConfigurationManager.AppSettings.GetValues("failedTests")[0];
Browser = ConfigurationManager.AppSettings["WebDriver"];
DirectoryInfo directory = new DirectoryInfo(baseDirectory);
DirectoryInfo[] subdirs = directory.GetDirectories();
if (System.IO.Directory.GetDirectories(baseDirectory).Length == 0)
{
screenshotDirectory = baseDirectory + #"\" + (DateTime.Now.ToString("yyyy_MM_dd_hh_mm") + "_" + Browser);
Directory.CreateDirectory(screenshotDirectory);
}
Here is the code for taking screenshot:
public void takeScreenshot(string filename)
{
string fname = filename + ".jpg";
string screenshot = screenshotDirectory + #"\" + fname;
Screenshot ss = ((ITakesScreenshot)WebDriver).GetScreenshot();
byte[] image = ss.AsByteArray;
using (MemoryStream ms = new MemoryStream(image))
{
Image i = Image.FromStream(ms);
i.Save(screenshot);
}
I assume that the error is at this i.Save(screenshot) call, but I was not able to resolve it.
I have reason to believe (from experience) that your issue comes about as a result of the stream being destroyed while it is being saved (the using statement).
Things to be aware of:
Write permissions wherever you are saving the image
Make sure the path is correct - this will throw a GDI+ exception and is very misleading, verify your path, try a temporary directory instead of creating your custom image directory to rule this one out.
Make sure the height of the image is not bigger than (65534px)
You can verify this by looking at the size:
var bitmapTemp = new Bitmap(stream);
Console.WriteLine(bitmapTemp.Height);
Here's some code that destroys the stream only after the image is saved:
public static Screenshot GetScreenshot(ChromeDriver driver)
{
Screenshot ss = ((ITakesScreenshot)driver).GetScreenshot();
return ss;
}
public static void SaveScreenshot(byte[] byteArray, string location)
{
var stream = new MemoryStream(byteArray);
var img = Image.FromStream(stream);
img.Save(location);
stream.Dispose();
}
And use the functions like so:
var path = AppDomain.CurrentDomain.BaseDirectory;
var ss = GetScreenshot(driver);
SaveScreenshot(ss.AsByteArray, path + "imagename.jpg");
Thanks for your inputs AntonB.
I have considered your points and tried differently and got the solution.
i have used [SetUpFixture], [OneTimeSetUp] and [OneTimeTearDown] to create folder only once and it solved the problem.
Here is the code:
[SetUpFixture]
public class Config
{
public Config()
{
}
public string baseDirectory;
public static string screenshotDirectory;
[OneTimeSetUp]
public void SetUp()
{
Console.WriteLine("Creating a folder to capture failed scenarios");
baseDirectory = AppDomain.CurrentDomain.BaseDirectory + #"\" + ConfigurationManager.AppSettings.GetValues("failedTests")[0];
string Browser = ConfigurationManager.AppSettings["WebDriver"];
screenshotDirectory = baseDirectory + #"\" + (DateTime.Now.ToString("yyyy_MM_dd_hh_mm") + "_" + Browser);
Directory.CreateDirectory(screenshotDirectory);
}
[OneTimeTearDown]
public void TearDown()
{
}
}

Categories

Resources