# Efficient image Import

I have several sets of 20 images I need to import.

Each time they have different categories.

"Name"<>"Cat"<>"No"

Electric B 1 for example.

I need to create lists that contain the images for those different categories.

As of now this is what i am doing :

importOne[set_, cat_, num_] :=
ImageResize[
Import[FileNameJoin[{NotebookDirectory[], set,
set <> cat <> ToString[num] <> ".png"}]], 128]

ElectricB = importOne["Electric", "B", #] & /@ Range[6];
ElectricC = importOne["Electric", "C", #] & /@ Range[5];
ElectricN = importOne["Electric", "N", #] & /@ Range[5];
ElectricE = importOne["Electric", "E", #] & /@ Range[4];


And then, thanks to previous question/answers :

What would be a more efficient mean to import automatically for different sets ?

As of now I need to repeat this operation for each set.

-
As in "How do I import them and let Mathematica do the categorization based on the filename"? –  David Feb 1 '12 at 19:06
@David yes :-) I remember someone showed a trick to import all files which name start with ... in a question that was closed and he removed his answer... –  500 Feb 1 '12 at 19:18

Make your filenames unambiguously parsable, e.g. by consistently using some delimeters like underscores or something. A typical file name can look like "Electric_B_3.png". EDIT If you have no control over the file names, use string patterns as described by other answers, but in the long-term you may benefit from creating your own robust naming scheme END EDIT

Then write a function that would parse a single file name, something like:

fileNameParse[fname_String, delim_String: "_"] :=
StringSplit[FileBaseName[fname], delim]


Then, Map it on FileNames["*.png", {your-dir}].

Finally, apply your importOne on the level one:

importOne@@@Map[fileNameParse, FileNames["*.png", {your-dir}]]


Since you have the result of Map available as well, you can regroup them any way you want. You can, for example, Map a function {#, importOne@@#}&, rather than just using importOne@@@.... Then, you could use GatherBy or any other means to regroup and collect your images according to the parts of their filenames.

EDIT

Here is a self-contained example ( I use text files, but this doesn't matter):

ClearAll[fileNameParse, fileNameMake, importOne, $dir]; fileNameParse[fname_String, delim_String: "_"] := StringSplit[FileBaseName[fname], delim]; fileNameMake[pieces_List, delim_String: "_", ext_String: ".txt"] := StringJoin[Append[Riffle[pieces, "_"], ".txt"]]; importOne[set_, cat_, num_, dir_:$dir] :=
Import[FileNameJoin[{dir, fileNameMake[{set, cat, num}]}]];


We now create a temporary directory:

$dir = FileNameJoin[{$TemporaryDirectory, "ImportTest"}];
If[! FileExistsQ[$dir], CreateDirectory[$dir]];


Create sample files:

MapIndexed[
Export[#, "Test" <> ToString[#2], "Text"] &,
Flatten[
Outer[
FileNameJoin[{$dir, fileNameMake[{##}]}] &, {"Electric"}, {"A", "B", "C"}, {"1", "2", "3"} ]]];  import them: imported = Map[{#, importOne @@ #} &, fileNameParse /@ FileNames["*.txt", {$dir}]]

(*
==>

{{{"Electric", "A", "1"},  "Test{1}"}, {{"Electric", "A", "2"}, "Test{2}"},
{{"Electric", "A", "3"},  "Test{3}"}, {{"Electric", "B", "1"}, "Test{4}"},
{{"Electric", "B", "2"},  "Test{5}"}, {{"Electric", "B", "3"},  "Test{6}"},
{{"Electric", "C", "1"},  "Test{7}"}, {{"Electric", "C", "2"},  "Test{8}"},
{{"Electric", "C", "3"}, "Test{9}"}
}
*)


You can now, for example, group them according to whatever parts of their file names you wish:

GatherBy[imported , #[[1, 2]] &][[1]]

(*
==>

{{{"Electric", "A", "1"}, "Test{1}"}, {{"Electric", "A", "2"}, "Test{2}"},
{{"Electric", "A", "3"}, "Test{3}"}}

*)

-
Thank You very much for the detailed explanation. –  500 Feb 2 '12 at 1:46
@500 No problem - glad I could help. –  Leonid Shifrin Feb 2 '12 at 1:47

Here is one approach. First set your working directory with SetDirectory, then:

names = FileNames["*.png"];

groups = GatherBy[names, StringTake[#, {-6}] &];

Evaluate[
electric[StringTake[#, {-6}]] & /@ groups[[All, 1]]
] = Map[Import[#] ~ImageResize~ 128 &, groups, {2}];


Now access image lists like:

electric["B"]


This relies on manually picking a string position to index by, here {-6}. Leonid's method would be more robust for future naming.

-
Thank You, I must say evaluate remains quite mysterious to me yet :-) –  500 Feb 2 '12 at 2:07
@500 Evaluate is used when you need something to evaluate first, so that a function does not see the unevaluated form. In this case, I needed Set (=) not to see electric[StringTake[#, {-6}]] ... but instead see {electric["B"], electric["C"] ...}. –  Mr.Wizard Feb 2 '12 at 3:17
Thank You very much ! –  500 Feb 2 '12 at 23:52

This is how you import all files satisfying a certain pattern:

1. Import the directory. This will give you a list of all the files it contains.

fileList = Import[NotebookDirectory[] <> "/pics"]

{"fooA1bar.png", "fooA2bar.png", "fooA3bar.png", "fooBbar.png"}


2. For convenience, define a function that returns TRUE if the argument starts with a certain string. I use a string pattern here for simplicity. (Regex would be the normal way for me, but people complained in other questions.)

startsWithQ[string_, start_] := StringMatchQ[
string,
start ~~ ___
]


3. You can now use this function to create another function that extracts all the files starting with some string from the original file list (i.e. the list you obtained by importing the directory).

filterFileList[startsWith_] := Select[
fileList,
startsWithQ[#, startsWith] &
]


4. Apply that function! :-)

filterFileList["fooA"]

{"fooA1bar.png", "fooA2bar.png", "fooA3bar.png"}


As you can see, only the files starting with fooA remain. This method does of course work with other string patterns (or regular expressions), simply replace the pattern ("start ~~ ___") according to your needs.

5. Import the pictures by applying Import to every list element and your're done:

getPictures[filelist_] := Import[picpath <> "/" <> #] & /@ filelist
getPictures[filterFileList["fooA"]]


As mentioned by Spartacus, you could also use FileNames to get around many of the steps above. On the other hand, using patterns is much more flexible, and can match quite complicated filenames effectively.

-