Often, when doing replaces of this complexity, instead of using preg_replace, use preg_match_all(). To match the img tag and surrounding <a> tag if it exists, try something like:
preg_match_all( '/(\<\s*a.*href=["\'](.*)["\'].*\>\s*)?(\<img.*\>)/siU', $html, $matches );
for( $j = 0; $j < $matches[0]; $j++ ) {
$entire_match = $matches[0][$j];
$link_tag = $matches[1][$j];
$img_tag = $matches[2][$j];
//$changed_img_tag = 'do your parsing here';
if ( !$link_tag ) { //skip if this image has a link
$html = str_replace($entire_match, $changed_img_tag, $html);
}
}
note: I didn't test the regexp above, i typed it out just for the post. Let me know if it doesn't work.