Some background info on this behavior: Bitmap uses a memory-mapped file to access the pixels in the bitmap. That's a very basic facility in the Windows API, it allows very efficient mapping of memory to file data. Data is read from the file only when the program read the memory, the virtual memory pages don't take any space in the Windows paging file.
The exact same mechanism is used to load .NET assemblies. It is the memory mapping that puts a lock on the file. Which is basically why assemblies are locked when they are used in a .NET program. The Image.Dispose() method releases the lock. Fighting the lock often indicates that you are forgetting to dispose your bitmaps. Very important, forgetting to call Dispose() doesn't often cause problems for .NET classes, except for Bitmap since it can need so much (unmanaged) memory.
Yes, FromStream() prevents the class from making this optimization. The cost is significant, you'll need double the memory when the bitmap is loaded. This will be a problem when the bitmap is large, you're skirting OOM when the program has been running for a while (fragmenting the address space) and its not running on a 64-bit operating system. Definitely avoid doing this if the bitmap's Width x Height x 4 >= 45 MB, give or take.
Some code, you don't have to jump through the CopyStream hoop:
public static Image LoadImageNoLock(string path) {
using (var ms = new MemoryStream(File.ReadAllBytes(path))) {
return Image.FromStream(ms);
}
}